2016-03-10 17 views
5

Voglio scrivere uno script che scorre uscita (array forse?) Di un'uscita della shell: comando psBash Script loop attraverso l'uscita della shell

Qui è in guscio output dei comandi:

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 

convertirlo in script bash (Snippet):

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

Ma l'uscita diventa:

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

Come si esegue il ciclo di ogni riga come nell'output della shell ma nello script bash?

risposta

16

Mai for ciclo sopra i risultati di un comando shell se si desidera elaborare riga per riga a meno che non si sta modificando il valore del separatore di campo interno $IFS-\n. Questo perché le linee avranno oggetto della divisione divisione che porta ai risultati effettivi che stai vedendo. Significato se per esempio avete un file in questo modo:

foo bar 
hello world 

Il seguente ciclo for

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

ti dà:

foo 
bar 
hello 
world 

Anche se si utilizza IFS='\n' le linee potrebbero ancora ottenere soggetto di Filename expansion


Si consiglia di utilizzare while + read invece perché read legge riga per riga.

Inoltre, vorrei utilizzare pgrep se si cercano i pid appartenenti a un determinato binario. Tuttavia, dal momento che Python potrebbe apparire come binari diversi, come python2.7 o python3.4, suggerisco di passare -f a pgrep che lo fa cercare nell'intera riga di comando anziché cercare solo i binari chiamati python. Ma questo troverà anche processi che sono stati avviati come cat foo.py. Sei stato avvertito! Alla fine puoi perfezionare la regex passata a pgrep come desideri.

Esempio:

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

o se si desidera anche il nome del processo:

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

Se si desidera che il nome del processo e il pid in variabili distinte:

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

È vedere, read offre un modo flessibile e stabile per elaborare l'output di un comando riga per riga.


Btw, se si preferisce la linea di comando ps .. | grep sopra pgrep utilizzare il seguente ciclo:

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 

Nota come ho cambiato l'ordine delle etime e cmd. Pertanto, per poter leggere cmd, che può contenere spazi bianchi, in una singola variabile. Funziona perché read divide la riga in variabili, tante volte quante sono le variabili specificate. La parte restante della riga, che potrebbe includere spazi bianchi, verrà assegnata all'ultima variabile che è stata specificata nella riga di comando.

+0

Suppongo che intendessi "pgrep" e non "prep"? – adic26

+0

Oh, certo ....... Btw, se hai bisogno dell'etime, usa 'ps -p" $ pid "-o etime' nel ciclo. Ovviamente puoi anche usare il tuo 'ps .. | grep 'command line, ma ancora pipe a' while read ... '... – hek2mgl

+0

Anche se si imposta' IFS', l'espansione della sostituzione del comando è ancora soggetta all'espansione del pathname, quindi il ciclo 'for' è solo semplicemente sbagliato – chepner

1

Quando si utilizza for loop in bash suddivide la lista in default whitespaces, questo può essere adattata utilizzando il cosiddetto campo interno Separatore o corto IFS.

IFS L'interno Separatore campo che viene usato per la suddivisione dopo espansione e per dividere le linee in parole con il comando incorporato leggere. Il valore predefinito è "".

Per il tuo esempio ci sarebbe bisogno di dire a IFS utilizzare new-lines come punto di rottura.

IFS=$`\n` 

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

Questo esempio restituisce il seguente output sulla mia macchina.

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

La sostituzione di comando è ancora soggetta all'espansione del percorso. [Fallo bene] (http://mywiki.wooledge.org/BashFAQ/001), e usa un ciclo 'while'. – chepner

0

ho trovato che si può fare questo basta usare le virgolette:

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

Ciò farà risparmiare ogni riga alla matrice piuttosto che ogni elemento.

+0

Questo metterà l'intero output dal comando in un singolo elemento dell'array. –

+0

@EtanReisner Grazie per la cattura. – jkdba