OVH Cloud OVH Cloud

[newbie] programmation bash

8 réponses
Avatar
alouestdusoleil
Bonjour à tous,

Je souhaiterais traiter ligne par ligne le résultat d'une commande

Je m'explique :

[gv@spirou gv]$ ps
PID TTY TIME CMD
2005 pts/2 00:00:00 bash
2023 pts/2 00:00:00 ps
[gv@spirou gv]$ PS=$(ps)
[gv@spirou gv]$ echo $PS
PID TTY TIME CMD 2005 pts/2 00:00:00 bash 2024 pts/2 00:00:00 ps
[gv@spirou gv]$ for ligne in $PS ; do echo $ligne ; done
PID
TTY
TIME
CMD
2005
pts/2
00:00:00
bash
2024
pts/2
00:00:00
ps

En fait, j'ignore si le résultat de la commande est stocké dans la variable de
manière à ce que les lignes gardent leur individualité, ou bien si le tout est
«aplati»

Faut-il jouer sur la valeur de la variable IFS ? et comment ?

Merci d'avance,
--
^^
(_____/°°-ç Gauthier
| \_`-"
)/@mmm|| alouest.du.soleil@NOSPAMskynet.be
\nn \nn
FOE-Belgium : http://www.amisdelaterre.be

8 réponses

Avatar
Stephane Dupille
Bonjour à tous,


Salut !

Je souhaiterais traiter ligne par ligne le résultat d'une commande
Je m'explique :
[ gv]$ for ligne in $PS ; do echo $ligne ; done
PID
TTY
< snip >


Normal. For va séparer les mots, pas les lignes.

En fait, j'ignore si le résultat de la commande est stocké dans la
variable de manière à ce que les lignes gardent leur individualité,
ou bien si le tout est «aplati»


Pas compris la question.

Faut-il jouer sur la valeur de la variable IFS ? et comment ?


Deux solutions. Soit en changeant IFS :
~> IFS='' ; for ligne in $(ps) ; do echo $ligne ; done
PID TTY TIME CMD
4993 pty/s0 00:00:00 zsh
5170 pty/s0 00:00:00 ps

Soit en utilisant read (ce qui à mon avis est meilleur) :
~> ps | while read ligne ; do echo $ligne ; done
PID TTY TIME CMD
4993 pty/s0 00:00:00 zsh
5173 pty/s0 00:00:00 ps

Mes exemples sont avec zsh (mais comme vous n'aviez par précisé le
shell).

Sinon vos lignes sont trop longues, et votre signature trop longue
également.

--
(Pour mesurer l'intelligence dans fufe) Facile: un test de Turing. Tu
prends une personne dans un groupe sensé, une personne dans fufe. Dès
que tu arrives à repérer le trolleur tu détruis le groupe.
-+- Ol in Guide du Neuneu Usenet : Maffacre à la fufonneuse -+-

Avatar
Stephane Chazelas
2003/11/14, 09:26(+01), Stephane Dupille:
[...]
Normal. For va séparer les mots, pas les lignes.


C'est pas for, c'est l'expansion de la variable $PS, certe
parce que cette expansion se fait après un "for", mais c'est le
cas dans la plupart des autres endroits (exceptions dans
[[...]], les assignments et après case, et pour le plupart des
shells sauf bash après les opérateurs de redirection).

Après expansion, le shell découpe en argument suivant IFS et
génère des fichiers si la variable contient des wildcards et
avec ksh93 fait en plus de la brace expansion. Donc, si

PS='*:{?,/*}' IFS=':'
$PS va etre transformé en une liste: la liste des fichiers
dans le répertoire courant (*), la liste des fichiers dont le
nom est formé d'un byte (?) dans le répertoire courant, la liste
des fichiers dans / (pour ksh93).

zsh fait exception ici (sauf quand il est en mode compatibilité
sh ou ksh). Pour lui, il faut lui dire explicitement de faire le
découpage ou la filename generation ($=PS pour le découpage,
$~PS pour la filename generation).


En fait, j'ignore si le résultat de la commande est stocké dans la
variable de manière à ce que les lignes gardent leur individualité,
ou bien si le tout est «aplati»


Pas compris la question.


PS contient le résultat de la commande, sans les caractères
newline de la fin. Ça déconne si l'output de la commande
contient des ''

Si la commande affiche $'anbncnnn', $PS va contenir
$'anbnc'.

Faut-il jouer sur la valeur de la variable IFS ? et comment ?



Par défaut IFS contient $' tn', si tu veux ne découper que sur
les newlines, faut metre $'n'. Mais il faut aussi désactiver la
"filename generation" (set -f).


Deux solutions. Soit en changeant IFS :
~> IFS='' ; for ligne in $(ps) ; do echo $ligne ; done
PID TTY TIME CMD
4993 pty/s0 00:00:00 zsh
5170 pty/s0 00:00:00 ps


Avec zsh, ça ne fait aucune différence, à moins qu'on ait activé
l'option shwordsplit.

De plus si, tu ne mets rien dans IFS, ça désactive le word
splitting au lieu de faire un découpage par ligne.

Soit en utilisant read (ce qui à mon avis est meilleur) :
~> ps | while read ligne ; do echo $ligne ; done
PID TTY TIME CMD
4993 pty/s0 00:00:00 zsh
5173 pty/s0 00:00:00 ps


Utiliser "IFS= read -r" au lieu de "read" si on veut un peu plus
de fidélité.

Mes exemples sont avec zsh (mais comme vous n'aviez par précisé le
shell).


Si, dans l'objet du message.

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]


Avatar
no
On Fri, 14 Nov 2003 10:50:07 +0100, Stephane Chazelas wrote:

2003/11/14, 09:26(+01), Stephane Dupille:
[...]
Normal. For va séparer les mots, pas les lignes.


... bash ... ksh93 ... sh ... ksh ... [insert your shell here]



Personnellement, dans ces cas, je prefère utiliser directement perl... :)


Avatar
alouest.du.soleil
Le Fri, 14 Nov 2003 10:50:07 +0100, Stephane Chazelas
a écrit:

Faut-il jouer sur la valeur de la variable IFS ? et comment ?



Par défaut IFS contient $' tn', si tu veux ne découper que sur
les newlines, faut metre $'n'. Mais il faut aussi désactiver la
"filename generation" (set -f).


Donc, si j'ai bien compris, $IFS comprend par défaut 3 caractères de
séparation, ce qui fait qu'il ne différencie pas les sauts de lignes des
espaces, et donc ne reconnait pas les lignes en tant que telles ?

Il y a un mécanisme d'"expansion" des caractères $IFS quand on utilise
la commande echo ? ça veut dire que chaque occurrence d'un de ces caractères
est remplacé par un espace ?

Mai alors comment expliquer ce qui suit :

[ gv]$ ps
PID TTY TIME CMD
1925 pts/1 00:00:00 bash
1939 pts/1 00:00:00 ps
[ gv]$ PS=$(ps)
[ gv]$ echo $PS
PID TTY TIME CMD 1925 pts/1 00:00:00 bash 1940 pts/1 00:00:00 ps
[ gv]$ IFS="n"
[ gv]$ echo $PS
PID TTY TIME CMD
1925 pts/1 00:00:00 bash
1940 pts/1 00:00:00 ps
[ gv]$ IFS=" nt"
[ gv]$ echo $PS
PID TTY TIME CMD
1925 p s/1 00:00:00 bash
1940 p s/1 00:00:00 ps


Pourquoi avec la valeur par défaut d'$IFS, ma variable se retrouve sur une
seule ligne, et que je ne retrouve pas ce comportement après avoir spécifié
IFS=" nt" ?

Autrement dit, comment rétablir la valeur par défaut ?
--
^^ Gauthier
(_____/°°-ç
| _`-"
)/@mmm||
nn nn FOE-Belgium : http://www.amisdelaterre.be



Avatar
Stephane Chazelas
2003/11/14, 22:25(+00), Gauthier:
[...]
Il y a un mécanisme d'"expansion" des caractères $IFS quand on utilise
la commande echo ? ça veut dire que chaque occurrence d'un de ces caractères
est remplacé par un espace ?
[...]


Non, "echo" n'expand rien du tout. echo affiche ses arguments
séparés par des espaces.

Si var=$'anb ctd' IFS=' tn'
echo $var

le shell (pas echo) découpe $var en "a", "b", "c", "d".
echo les affiche séparés par des espaces.

Mai alors comment expliquer ce qui suit :
[...]

[ gv]$ IFS="n"
[ gv]$ echo $PS
PID TTY TIME CMD
1925 pts/1 00:00:00 bash
1940 pts/1 00:00:00 ps


"n", c'est les caractères '' et 'n', pas un saut de ligne.
IFS="
"
ou
IFS=$'n'

pour avoir un saut de ligne.

[...]
Autrement dit, comment rétablir la valeur par défaut ?


unset IFS

(pour les shells POSIX). Enfin, ça ne veut pas dire qu'IFS va
contenir $' tn' après, ça veux dire qu'on aura le word
splitting par défaut (comme si IFS vallait $' tn').

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]

Avatar
alouest.du.soleil
Le Sat, 15 Nov 2003 01:15:26 +0100, Stephane Chazelas
a écrit:
2003/11/14, 22:25(+00), Gauthier:
[...]
Il y a un mécanisme d'"expansion" des caractères $IFS quand on utilise
la commande echo ? ça veut dire que chaque occurrence d'un de ces caractères
est remplacé par un espace ?
[...]


Non, "echo" n'expand rien du tout. echo affiche ses arguments
séparés par des espaces.


OK, ça commence à s'éclaircir, mais ça veut donc dire que dans ce cas, la
variable ("ses arguments") est une liste à plusieurs éléments ? Je croyais
qu'en faisant $PS=$(ps) , l'ensemble de la réponse serait une seule chaîne de
caractères, sauts de lignes inclus.

Si var=$'anb ctd' IFS=' tn'


Que signifie cette syntaxe variable=$'an etc.' au lieu de
variable='an etc.' ?

--
^^ Gauthier
(_____/°°-ç
| _`-"
)/@mmm||
nn nn FOE-Belgium : http://www.amisdelaterre.be


Avatar
Stephane Chazelas
2003/11/15, 09:18(+00), Gauthier:
[...]
Non, "echo" n'expand rien du tout. echo affiche ses arguments
séparés par des espaces.


OK, ça commence à s'éclaircir, mais ça veut donc dire que dans ce cas, la
variable ("ses arguments") est une liste à plusieurs éléments ? Je croyais
qu'en faisant $PS=$(ps) , l'ensemble de la réponse serait une seule chaîne de
caractères, sauts de lignes inclus.


En fait, les variables ont un type polymorphe en shell (et c'est
une sacrée m****). Pour bash et ksh, quand tu fais:

PS=$(ps)

Tu assignes à l'indice 0 du tableau PS l'output de "ps" (sans
les newlines de la fin) sous forme de string, pas de surprise.
Toutes les variables sont des tableaux de strings, mais c'est à
l'expansion qu'on va avoir des types differents. Suivant
l'endroit où va se trouver $PS (equivalent de ${PS[0]}), ça va
etre considéré comme un truc ou un autre.

Dans cmd $var
c'est une liste de fichiers (découpage, expansion des wildcards)

dans cmd "$var"
c'est une string. C'est pour ça que la plupart du temps (sauf si
justement on veut du word splitting et de la filename
generation), il *faut* mettre la variable entre double-quotes.

dans [[ $toto = $var ]]
c'est un patron

dans < $var
ça dépend des shells (enfin, c'est une string, sauf dans bash où
c'est un bug).

dans toto=$var
c'est une string (comme dans cmd "$var")

Ensuite, ya encore plein de ruses à la con pour accéder aux
autres éléments du tableau.

Le comportement de zsh est infiniment plus simple et clair là
dessus.

Donc, tu as raison, PS contient l'ensemble de la réponse d'une
seule chaîne de caractères, sauts de lignes inclus (sauf les
derniers), mais c'est à l'expansion (quand on fait référence à
la variable) que tout se joue, il faut alors jouer avec les
quotes, IFS et set -f pour avoir ce qu'on veut.


Si var=$'anb ctd' IFS=' tn'


Que signifie cette syntaxe variable=$'an etc.' au lieu de
variable='an etc.' ?


$'...' est une autre sorte de quote. Le shell expand les t, b,
n, 33... à l'intérieur, un peu comme les "..." de la plupart
des autres langages. On ne le trouve que dans ksh93, bash2 et
zsh.

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]


Avatar
alouest.du.soleil
OK, merci pour tous ces renseignements.
Finalement, Perl, c'est limpide...
--
^^ Gauthier
(_____/°°-ç
| _`-"
)/@mmm||
nn nn FOE-Belgium : http://www.amisdelaterre.be