2016-03-10 17 views
5

ich ein Skript schreiben will, das durch die Ausgangsschleifen (Array möglicherweise?) Eine Shell-Ausgabe: ps BefehlBash Script Schleife durch Shell-Ausgabe

Hier ist es in Shell-Befehl ausgegeben:

ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh 
3089 python /var/www/atm_securit  37:02 
17116 python /var/www/atm_securit  00:01 
17119 python /var/www/atm_securit  00:01 
17122 python /var/www/atm_securit  00:01 
17125 python /var/www/atm_securit  00:00 

wandeln es in bash-Skript (Snippet):

for tbl in $(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh) 
do 
    echo $tbl 
done 

Aber der Ausgang wird:

3089 
python 
/var/www/atm_securit 
38:06 
17438 
python 
/var/www/atm_securit 
00:02 
17448 
python 
/var/www/atm_securit 
00:01 

Wie durchlaufe ich jede Zeile wie in der Shell-Ausgabe, aber im Bash-Skript?

Antwort

16

Nie for Schleife über die Ergebnisse einer Shell-Befehl möchten, wenn Sie es Zeile für Zeile verarbeiten, es sei denn Sie werden den Wert des internen Feldtrenn$IFS-\n ändern. Dies liegt daran, dass die Zeilen das Thema Wortspaltung bekommen, was zu den tatsächlichen Ergebnissen führt, die Sie sehen. Bedeutung, wenn Sie zum Beispiel eine Datei wie folgt aussehen:

foo bar 
hello world 

Die folgende for-Schleife

for i in $(cat file); do 
    echo "$i" 
done 

gibt Ihnen:

foo 
bar 
hello 
world 

Auch wenn Sie IFS='\n' die Linien verwenden könnten noch unter dem Vorbehalt erhalten von Filename expansion


Ich empfehle stattdessen while + read zu verwenden, weil read zeilenweise liest.

Darüber hinaus würde ich pgrep verwenden, wenn Sie nach PIDS suchen, die zu einer bestimmten Binärdatei gehören. Da Python jedoch als verschiedene Binärdateien angezeigt werden kann, wie python2.7 oder python3.4, empfehle ich, -f an pgrep zu übergeben, wodurch die gesamte Befehlszeile gesucht wird und nicht nur nach Binärdateien mit der Bezeichnung python gesucht wird. Es werden aber auch Prozesse gefunden, die wie cat foo.py gestartet wurden. Du wurdest gewarnt! Am Ende können Sie die Regex verfeinern, die wie gewünscht an pgrep übergeben wird.

Beispiel:

pgrep -f python | while read -r pid ; do 
    echo "$pid" 
done 

oder wenn Sie wollen auch den Prozessname:

pgrep -af python | while read -r line ; do 
    echo "$line" 
done 

Wenn Sie den Prozessnamen möchten und die pid in separaten Variablen:

pgrep -af python | while read -r pid cmd ; do 
    echo "pid: $pid, cmd: $cmd" 
done 

Sie siehe read bietet eine flexible und stabile Möglichkeit, die Ausgabe eines Befehls Zeile für Zeile zu verarbeiten.


Btw, wenn Sie Ihre ps .. | grep Befehlszeile über pgrep verwenden Sie die folgende Schleife bevorzugen:

ps -ewo pid,etime,cmd | grep python | grep -v grep | grep -v sh \ 
    | while read -r pid etime cmd ; do 
    echo "$pid $cmd $etime" 
done 

Beachten Sie, wie ich die Reihenfolge der etime und cmd geändert. So kann cmd, die Whitespace enthalten kann, in eine einzige Variable gelesen werden. Das funktioniert, weil read die Zeile in Variablen aufteilt, so oft Sie Variablen angegeben haben. Der verbleibende Teil der Zeile - möglicherweise einschließlich Leerzeichen - wird der letzten Variablen zugewiesen, die in der Befehlszeile angegeben wurde.

+0

Ich nehme an, du meintest 'pgrep' nicht 'prep'? – adic26

+0

Oh, sicher ....... Btw, wenn Sie die Zeit benötigen, verwenden Sie "ps -p" $ pid "-o etime" in der Schleife. Natürlich können Sie auch Ihre 'ps .. | grep 'command line, aber pipe immer noch zu' while read ... '... – hek2mgl

+0

Selbst wenn Sie' IFS' setzen, unterliegt die Erweiterung der Befehlsersetzung immer noch der Pfadnamenerweiterung, also ist die 'for' Schleife einfach einfach falsch. – chepner

1

Wenn for Schleife in bash verwendet sie die angegebene Liste von Standard spalten von whitespaces, kann dies mit Hilfe des so genannten internes Feld Seperator oder kurz IFS angepasst werden.

IFS das interne Feld Separator, die für einzelne Wörter aufgespalten nach Expansion verwendet wird und Linien in Worte mit dem Lesen eingebauten Befehl zu spalten. Der Standardwert ist "".

Für Ihr Beispiel würden wir brauchen IFS zu sagen new-lines als Bruchstelle zu verwenden.

IFS=$`\n` 

for tbl in $(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh) 
do 
    echo $tbl 
done 

Dieses Beispiel gibt die folgende Ausgabe auf meinem Computer zurück.

668 /usr/bin/python /usr/bin/ud 03:05:54 
27892 python       00:01 
+0

Die Befehlsersetzung unterliegt weiterhin der Erweiterung des Pfadnamens. [Mach es richtig] (http://mywiki.wooledge.org/BashFAQ/001) und verwende eine "while" -Schleife. – chepner

0

Ich fand Sie diese doppelte Anführungszeichen verwenden Sie einfach tun:

while read -r proc; do 
    #do work 
done <<< "$(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh)" 

Diese jede Zeile in dem Array speichern, anstatt jedes Element.

+0

Dadurch wird die gesamte Ausgabe des Befehls in ein einzelnes Element des Arrays geschrieben. –

+0

@EtanReisner Danke für den Fang. – jkdba