1. Dashboard
  2. Forum
    1. Unerledigte Themen
  3. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team-Mitglieder
    4. Trophäen
    5. Mitgliedersuche
  4. Tutorial Bereich
  • Deutsch
  • Anmelden
  • Registrieren
  • Suche
Dieses Thema
  1. Informatik Forum
  2. Software und Anwendungen
  3. Betriebssysteme

Bash/Kommandozeile - String der durch Leerzeichen getrennt ist zerlegen

    • Linux
  • fieselschweif
  • 15. Januar 2009 um 11:48
  • Unerledigt
  • fieselschweif
    5
    fieselschweif
    Mitglied
    Reaktionen
    1
    Punkte
    226
    Beiträge
    42
    • 15. Januar 2009 um 11:48
    • #1

    Hallo Linux-Freunde,

    ich habe folgendes Problem:
    gegeben ist ein String der mehrere Felder besitzt, getrennt durch Leerzeichen. Nun möchte ich ganz einfach das n-te Feld dieses Strings ausgeben lassen. Wie mach ich das am geschicktesten?
    Kann mir kaum vorstellen dass man sich hier selbst einen Parser basteln muss (auch wenns nicht sonderlich viel Arbeit wäre), dieser Mechanismus wird ja auch gebraucht um die Kommandozeilenargumente beim Starten an argv*[] durchzureichen.

    Also ein konkretes Beispiel:

    Code
    $ df -h | grep /dev/md0
    /dev/md0              228G  117G  100G  54% /

    Nun will ich mir von dieser einen Zeile das 5. Feld (also "54%" in dem Fall) ausgeben lassen.

    Hat wer eine Ahnung wie das einfach geht? Ich schätze mal es gibt schon ein Kommando dafür.
    Wenn jemand eine regexp-Lösung hat (sollte ja auch damit leicht gehen), auch fein :winking_face:

    Schöne Grüße,
    fieselschweif

    http://happyvimming.blogspot.com/
    -----
    Etwas zusätzlichen Speicher gefällig?
    "Signetics 25120 - Fully Encoded, 9046xN Random Access Write-Only-Memory" (Datenblatt lesen!)

    Einmal editiert, zuletzt von fieselschweif (15. Januar 2009 um 16:07) aus folgendem Grund: typo

  • Paulchen
    1
    Paulchen
    Gast
    • 15. Januar 2009 um 12:05
    • #2
    Code
    df -h | grep /dev/md0 | awk '{print $5}'


    Man könnte auch cut verwenden (-> Manpage) oder sed mit einer Backlink References. Da weiß ich aber in beiden Fällen die exakte Syntax nicht auswendig.

  • fieselschweif
    5
    fieselschweif
    Mitglied
    Reaktionen
    1
    Punkte
    226
    Beiträge
    42
    • 15. Januar 2009 um 12:27
    • #3
    Zitat von Paulchen
    Code
    df -h | grep /dev/md0 | awk '{print $5}'

    Man könnte auch cut verwenden (-> Manpage) oder sed mit einer Backlink References. Da weiß ich aber in beiden Fällen die exakte Syntax nicht auswendig.


    Die Firma dankt :face_with_rolling_eyes:
    Es wär mal interessant sich näher mit sed und awk zu beschäftigen. Kannst du Literatur hierfür empfehlen?

    http://happyvimming.blogspot.com/
    -----
    Etwas zusätzlichen Speicher gefällig?
    "Signetics 25120 - Fully Encoded, 9046xN Random Access Write-Only-Memory" (Datenblatt lesen!)

  • Jensi
    28
    Jensi
    Mitglied
    Reaktionen
    141
    Punkte
    8.486
    Beiträge
    1.649
    • 15. Januar 2009 um 12:50
    • #4

    Oder man verwendet ein einfaches C-Progamm:

    C
    #include <stdio.h>
    #include <sys/vfs.h>
    #include <libgen.h>
    
    
    #define FAIL 1
    
    
    void usage(char *argv[]);
    
    
    int main(int argc, char *argv[]) {
    	int retval;
    	struct statfs s;
    
    
    	long total_blocks = 0;
    	long free_blocks  = 0;
    
    
    	double percent_free = 0.0;
    
    
    	if (argc < 2) {
    		usage(argv);
    		return(FAIL);
    	}
    
    
    	/* Get file system statistics */
    	retval = statfs(argv[1], &s);
    
    
    	if (retval == 0) {
    		total_blocks = s.f_blocks;
    		free_blocks  = s.f_bfree;
    
    
    		if (total_blocks == 0) {
    			printf("Error: Total number of blocks returned zero.\n");
    			return(FAIL);
    		}
    
    
    		percent_free = (double) free_blocks / total_blocks * 100;
    
    
    		printf("%.1f%%\n", percent_free);
    
    
    	} else {
    		printf("Error: Couldn't retrieve file system statistics for \"%s\".\n", argv[1]);
    	}
    
    
    	return(retval);
    }
    
    
    void usage(char *argv[]) {
    	printf("Usage:  %s path\n", basename(argv[0]));
    	printf("\n");
    	printf("        Displays usage of a mounted file system (in percent).\n");
    	printf("\n");
    	printf("        Path must be any path or file (e. g. the mount point itself)\n");
    	printf("        of the file system for which the usage should be displayed.\n");
    	printf("\n");
    	printf("        If you specify a device file (e. g. /dev/hda1), you won't get\n");
    	printf("        the usage of the file system on /dev/hda1, but the usage of the\n");
    	printf("        file system in which the device node /dev/hda1 resides.\n");
    }
    Alles anzeigen
  • Kampi
    27
    Kampi
    Mitglied
    Reaktionen
    193
    Punkte
    7.828
    Beiträge
    1.468
    • 15. Januar 2009 um 14:07
    • #5
    Zitat von Jensi
    Code
    #define FAIL 1


    wie waers mit "EXIT_FAILURE"?

    Zitat von Jensi
    Code
    } else {
            printf("Error: Couldn't retrieve file system statistics for \"%s\".\n", argv[1]);
        }

    hm, strerror/warn/err/perror/what ever. wenn schon was errno setzt, sollte man das auch nutzen.

    sonst wuerd ich wohl "cut" nehmen.

    Willfähriges Mitglied des Fefe-Zeitbinder-Botnets und der Open Source Tea Party.

  • fieselschweif
    5
    fieselschweif
    Mitglied
    Reaktionen
    1
    Punkte
    226
    Beiträge
    42
    • 15. Januar 2009 um 16:06
    • #6

    Die C-Lösung ist auch recht nett, danke dafür!
    Verwenden werde ich sie aber nicht, weil ich ich ja damit (vermutlich) die exakt selbe Berechnung wie df nochmal implementiere und ich generell nichts davon halte das Rad neu zu erfinden, vor allem wenns schon genug Tools gibt die einem die gewünschte Information ausspucken (unter Windows wär das wieder was anderes) und die Performance ja in dem Fall auch völlig egal ist.

    Da ist mir ein schicker Einzeiler mit Pipes lieber :grinning_squinting_face:

    http://happyvimming.blogspot.com/
    -----
    Etwas zusätzlichen Speicher gefällig?
    "Signetics 25120 - Fully Encoded, 9046xN Random Access Write-Only-Memory" (Datenblatt lesen!)

  • Jensi
    28
    Jensi
    Mitglied
    Reaktionen
    141
    Punkte
    8.486
    Beiträge
    1.649
    • 15. Januar 2009 um 21:52
    • #7
    Zitat von Kampi

    wie waers mit "EXIT_FAILURE"?

    hm, strerror/warn/err/perror/what ever. wenn schon was errno setzt, sollte man das auch nutzen.


    OMG... Sysprog-Tutor? Also für die Sysprog-Tutoren unter uns:

    Diff
    --- old.c	2009-01-15 21:34:39.000000000 +0100
    +++ fsfree.c	2009-01-15 21:40:15.000000000 +0100
    @@ -1,8 +1,9 @@
     #include <stdio.h>
    +#include <stdlib.h>
     #include <sys/vfs.h>
     #include <libgen.h>
     
    -#define FAIL 1
    +#define DODEL_PRINTF(...) if (printf(__VA_ARGS__) < 0) { fprintf(stderr, "Help!\n"); exit(EXIT_FAILURE); }
     
     void usage(char *argv[]);
     
    @@ -17,7 +18,7 @@
     
     	if (argc < 2) {
     		usage(argv);
    -		return(FAIL);
    +		return(EXIT_FAILURE);
     	}
     
     	/* Get file system statistics */
    @@ -28,16 +29,17 @@
     		free_blocks  = s.f_bfree;
     
     		if (total_blocks == 0) {
    -			printf("Error: Total number of blocks returned zero.\n");
    -			return(FAIL);
    +			DODEL_PRINTF("Error: Total number of blocks returned zero.\n");
    +			return(EXIT_FAILURE);
     		}
     
     		percent_free = (double) free_blocks / total_blocks * 100;
     
    -		printf("%.1f%%\n", percent_free);
    +		DODEL_PRINTF("%.1f%%\n", percent_free);
     
     	} else {
    -		printf("Error: Couldn't retrieve file system statistics for \"%s\".\n", argv[1]);
    +		perror("statfs");
    +		exit(EXIT_FAILURE);
     	}
     
     	return(retval);
    Alles anzeigen
    Zitat von fieselschweif

    vor allem wenns schon genug Tools gibt die einem die gewünschte Information ausspucken


    Es spuckt halt leider nicht jedes Tool die Information in einer leicht nutzbaren Form aus - spätestens, wenn man dann dabei ist, im hundertsten Script die Zeile

    Code
    ADDR=`ifconfig ${IFACE} | grep "inet addr" | sed -e "s/.*inet addr:\([^[:space:]]*\).*/\1/"`


    zu schreiben, kommt man drauf, daß sowas wie

    Code
    ADDR=`ifdata -pa eth1`


    eigentlich doch eleganter ist.

    Außerdem kann es immer sein, daß Sachen auf einmal nicht mehr funktionieren, wenn sich das Ausgabeformat der Tools, aus denen man die Informationen auf die Art rausparst, irgendwann mal ganz minimal ändert.

    Grad bei df kann dir das passieren - "/dev/md0" geht sich ja noch leicht in einer Zeile aus, aber wenn der Pfad der device node lang ist, steht der Pfad in einer Zeile und die dazugehörigen Werte erst in der nächsten:

    Code
    /dev/mapper/server-root
                           90G  7.2G   78G   9% /


    ... und schon funktioniert der Pipeausdruck nicht mehr (und mit sed allein kann man das AFAIK gar nicht reparieren, weils zeilenbasiert arbeitet).

  • Kampi
    27
    Kampi
    Mitglied
    Reaktionen
    193
    Punkte
    7.828
    Beiträge
    1.468
    • 15. Januar 2009 um 22:27
    • #8
    Zitat von Jensi

    OMG... Sysprog-Tutor?


    noe, ich bin nur kein fan dieser selbstgestrickten fehlermeldungen. warum selber was zam schreiben wenn es dafuer wunderbare funktionen gibt? du wirst dir ja auch nicht jedes mal dein eigenes memcpy schreiben. es macht einfach sinn diese funktionen zu verwenden. und wenn es schon EXIT_FAILURE gibt, braucht man auch kein FAIL einfuehren. sysprog-printf ist natuerlich schwachsinn, aber davon hab ich ja nix geschrieben.

    also, no offence, vielleicht hab ich grad meinen kleinlichen tag:engel:

    Willfähriges Mitglied des Fefe-Zeitbinder-Botnets und der Open Source Tea Party.

  • skinner33
    9
    skinner33
    Mitglied
    Reaktionen
    22
    Punkte
    862
    Beiträge
    168
    • 15. Januar 2009 um 23:01
    • #9
    Zitat von Jensi

    OMG... Sysprog-Tutor? Also für die Sysprog-Tutoren unter uns:

    Code
    #define DODEL_PRINTF(...) if (printf(__VA_ARGS__) < 0) { fprintf(stderr, "Help!\n"); exit(EXIT_FAILURE); }


    das kompiliert aber mit gcc -ansi -pedantic -Wall -g -c *.c nicht ohne warning :winking_face:

    µC-Leitung

  • pfidschi
    2
    pfidschi
    Mitglied
    Punkte
    35
    Beiträge
    6
    • 15. Januar 2009 um 23:29
    • #10

    noch ein Variante

    Bash
    #!/bin/sh
    
    
    V=`df -h | grep /dev/md0`
    IFS=" "
    S=( $V )
    echo ${S[0]}
    echo ${S[1]}
    echo ${S[2]}
    echo ${S[3]}
    echo ${S[4]}
    echo ${S[5]}
    Alles anzeigen
  • Paulchen
    1
    Paulchen
    Gast
    • 15. Januar 2009 um 23:38
    • #11
    Zitat von pfidschi

    noch ein Variante

    Bash
    #!/bin/sh
    
    
    V=`df -h | grep /dev/md0`
    IFS=" "
    S=( $V )
    echo ${S[0]}
    echo ${S[1]}
    echo ${S[2]}
    echo ${S[3]}
    echo ${S[4]}
    echo ${S[5]}
    Alles anzeigen

    Welche Funktion hat die Variable IFS?

  • pfidschi
    2
    pfidschi
    Mitglied
    Punkte
    35
    Beiträge
    6
    • 16. Januar 2009 um 09:52
    • #12
    Zitat von Paulchen

    Welche Funktion hat die Variable IFS?

    Damit kann man das Trennzeichen definieren.

    Wenn der Inputtext
    eins,zwei,drei,vier
    lautet und du
    IFS=","
    setzt, dann kannst Du damit die einzelnen Wörter extrahieren.

  • fieselschweif
    5
    fieselschweif
    Mitglied
    Reaktionen
    1
    Punkte
    226
    Beiträge
    42
    • 16. Januar 2009 um 16:26
    • #13

    Jensi:
    Ja es kommt halt drauf an für was ich das Ding konkret brauch - solche Skripts schreib ich meistens nur für mich selbst, und dann weiß ich ganz einfach dass es funktioniert und somit ist die Sache gegessen :face_with_rolling_eyes:
    Falls das für möglichst viele Leute unter möglichst vielen Distributionen und Versionen von Tools funktionieren soll schauts natürlich anders aus, da würd ich u.U. durchaus eine C-Lösung vorziehen falls wirklich die Gefahr besteht dass die Ausgabe sich so großartig unterscheiden kann.

    Aufpassen muss man oft mit Locales, weil man sich dadurch nicht auf gewisse Wörter verlassen darf -

    Zitat von Jensi

    ADDR=`ifconfig ${IFACE} | grep "inet addr" | sed -e "s/.*inet addr:\([^[:space:]]*\).*/\1/"`


    funktioniert z.B. bei mir nicht, weil es mit deutschen Locales heißt "inet Adresse" statt "inet address" (ein

    Code
    grep "inet "

    würd schon reichen).
    Das ist natürlich auf der einen Seite zusätzliches Argument für C-Lösungen, auf der einen Seite ein Hinweis darauf dass man, WENN man schon Pipe-Lösungen macht wirklich darauf achten sollte dass man sich an Wörtern orientiert die auch sicher immer vorkommen.

    Zitat von Jensi

    Außerdem kann es immer sein, daß Sachen auf einmal nicht mehr funktionieren, wenn sich das Ausgabeformat der Tools, aus denen man die Informationen auf die Art rausparst, irgendwann mal ganz minimal ändert.

    Grad bei df kann dir das passieren - "/dev/md0" geht sich ja noch leicht in einer Zeile aus, aber wenn der Pfad der device node lang ist, steht der Pfad in einer Zeile und die dazugehörigen Werte erst in der nächsten:

    Code
    /dev/mapper/server-root
                           90G  7.2G   78G   9% /

    ... und schon funktioniert der Pipeausdruck nicht mehr (und mit sed allein kann man das AFAIK gar nicht reparieren, weils zeilenbasiert arbeitet).


    Das Problem lässt sich zum Beispiel noch relativ leicht lösen, hierfür gibts ja POSIX-Standards:

    Code
    df -P

    und schon bist sorgenfrei.
    Ist aber zugegeben auch nicht so leicht herauszufinden, was das genau macht, darüber schweigt sich die Manpage bei mir aus, erst durch info (was ich sonst eigentlich immer vermeide) findet mans raus, oder auch hier: http://www.gnu.org/software/coreu…invocation.html

    http://happyvimming.blogspot.com/
    -----
    Etwas zusätzlichen Speicher gefällig?
    "Signetics 25120 - Fully Encoded, 9046xN Random Access Write-Only-Memory" (Datenblatt lesen!)

  • Jensi
    28
    Jensi
    Mitglied
    Reaktionen
    141
    Punkte
    8.486
    Beiträge
    1.649
    • 16. Januar 2009 um 17:54
    • #14

    Hab grad zufällig noch was gefunden: mit "stat" kann man auch die Anzahl der Blocks (freie und gesamt) eines Dateisystems anzeigen:

    Code
    stat --file-system --format="%a/%b*100" /my/mount/point | bc -l
  • Maximilian Rupp 27. Dezember 2024 um 00:09

    Hat das Thema aus dem Forum Betriebssysteme nach Betriebssysteme verschoben.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!

Benutzerkonto erstellen Anmelden

Rechtliches

Impressum

Datenschutzerklärung

  • Alles
  • Dieses Thema
  • Dieses Forum
  • Seiten
  • Forum
  • Lexikon
  • Erweiterte Suche
  • Deutsch
  • English
Zitat speichern