web-dev-qa-db-fra.com

Boucle de script Bash via la sortie Shell

Je veux écrire un script qui boucle à travers la sortie (tableau éventuellement?) D'une commande Shell output: ps

Le voici dans la sortie de la commande Shell:

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

Convertissez-le en script bash (extrait):

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

Mais la sortie devient:

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

Comment puis-je parcourir chaque ligne comme dans la sortie Shell mais dans le script bash?

14
adic26

Ne jamais for parcourir les résultats d'une commande Shell si vous souhaitez la traiter ligne par ligne sauf si vous modifiez la valeur du séparateur de champ interne $IFS à \n. C'est parce que les lignes seront soumises à Découpage de mots qui conduit aux résultats réels que vous voyez. Par exemple, si vous avez un fichier comme celui-ci:

foo bar
hello world

Ce qui suit pour la boucle

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

vous donne:

foo
bar
hello
world

Même si vous utilisez IFS='\n' les lignes peuvent toujours faire l'objet de Extension du nom de fichier


Je recommande d'utiliser à la place while + read car read lit ligne par ligne.

De plus, j'utiliserais pgrep si vous recherchez des pids appartenant à un certain binaire. Cependant, puisque python peut apparaître sous forme de binaires différents, comme python2.7 ou python3.4 Je suggère de passer -f à pgrep, ce qui lui permet de rechercher toute la ligne de commande plutôt que de simplement rechercher des binaires appelés python. Mais cela trouvera également des processus qui ont été démarrés comme cat foo.py. Tu étais prévenu! À la fin, vous pouvez affiner le regex passé à pgrep comme vous le souhaitez.

Exemple:

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

ou si vous souhaitez également le nom du processus:

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

Si vous voulez que le nom du processus et le pid dans des variables distinctes:

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

Vous voyez, read offre un moyen flexible et stable de traiter la sortie d'une ligne de commande ligne par ligne.


Btw, si vous préférez votre ps .. | grep ligne de commande sur pgrep utilisez la boucle suivante:

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

Notez comment j'ai changé l'ordre de etime et cmd. Ainsi pour pouvoir lire cmd, qui peut contenir des espaces, en une seule variable. Cela fonctionne car read décomposera la ligne en variables, autant de fois que vous aurez spécifié de variables. La partie restante de la ligne - y compris éventuellement les espaces blancs - sera affectée à la dernière variable qui a été spécifiée dans la ligne de commande.

29
hek2mgl

Lorsque vous utilisez des boucles for dans bash, il divise la liste donnée par défaut par whitespaces, cela peut être adapté en utilisant ce que l'on appelle séparateur de champ interne ou court IFS.

IFS Séparateur de champ interne utilisé pour le fractionnement de mots après expansion et pour diviser des lignes en mots avec la commande intégrée read. La valeur par défaut est "".

Pour votre exemple, nous aurions besoin de dire à IFS d'utiliser new-lines comme point d'arrêt.

IFS=$`\n`

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

Cet exemple renvoie la sortie suivante sur ma machine.

  668 /usr/bin/python /usr/bin/ud    03:05:54
27892 python                            00:01
2
flazzarini

J'ai trouvé que vous pouvez le faire en utilisant simplement des guillemets doubles:

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

Cela enregistrera chaque ligne dans le tableau plutôt que chaque élément.

1
jkdba