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?
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.
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
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.