Subtilités de bash, je ne comprend pas tout
Fermé
gnugo
-
Modifié par gnugo le 12/10/2011 à 22:52
mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 13 novembre 2024 - 16 oct. 2011 à 22:39
mamiemando Messages postés 33357 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 13 novembre 2024 - 16 oct. 2011 à 22:39
A voir également:
- Subtilités de bash, je ne comprend pas tout
- Bash permission non accordée - Forum Shell
- Bash écrire dans un fichier - Forum Shell
- Bingo bash free - Télécharger - Divers Jeux
- Bash path - Astuces et Solutions
- Bash: adduser : commande introuvable ✓ - Forum Debian
6 réponses
mamiemando
Messages postés
33357
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
13 novembre 2024
7 805
Modifié par mamiemando le 13/10/2011 à 01:01
Modifié par mamiemando le 13/10/2011 à 01:01
comment savoir dans un script bash si une variable texte va être interprétée par une commande comme un seul ou plusieurs arguments?
En fait tu mets le doigts sur l'un des gros soucis de bash, que ce soit
- pour appeler une fonction shell à laquelle on passe plusieurs arguments (ce qui sépare les arguements... c'est le caractère espace, comme dans n'importe quelle commande shell !)
- pour passer un argument à un script ou à une fonction shell.
Variables $@ et $* (scripts et appels de fonctions shell)
C'est justement à cause de ce genre de considérations qu'on utilise par exemple $@ ou $*.
Exemple : ll.sh
Test :
Séquence d'échappement
De manière générale, à moins d'explicitement échapper un espace, celui-ci est considéré comme le métacaractère de séparation.
Ainsi :
... sont équivalentes. Par contre :
... signifie qu'on passe un argument "mon" et "répertoire" (et donc là ça va planter).
xargs, pipe, for et opérateur $(...) '...'
En fait la principale difficulté en shell c'est de comprendre comment itérer sur un ensemble d'élément.
Alors avant tout je précise tout de suite que contrairement à ce que la mise en forme (stupide), ce ne sont pas des apostrophes mais des backquotes (alt gr 7) qu'ils faut utiliser quand je parle de l'opérateur '...'.
En bash l'opérateur $(...) se comporte comme l'opérateur '...', il permet d'évaluer le contenu de l'opérande en tant que commande et de la substituer par son résultat.
Exemple :
Note qu'en bash (un shell particulier qui fournit des opérateurs et des fonctions supplémentaires par rapport à un shell générique) on peut également écrire :
Par la suite j'utiliserai $(...) pour éviter les ambiguïtés.
Si on utilise un opérateur capable de manipuler un flux sur /dev/stdin (l'entrée standard), comme grep, cut etc... on peut appliquer cette commande à chaque ligne reçu au niveau du flux.
Exemple :
Le processus dpkg -l liste les paquets installés ou partiellement installés sous debian. Chaque paquets installés est précédé d'un tag (ii). Le nom du paquet apparaît derrière le deuxième caractère espace.
Ainsi :
De manière générale quand un traitement peut être réalisée par un pipe c'est toujours mieux (plus performant).
Ici supposons que je veuille supprimer tous les paquets "rc" (partiellement installés, sous debian ça veut dire que le paquets est supprimé, mais pas ses fichiers de configuration).
... me renverra ces paquets. Maintenant je peux dire que je lance successivement "aptitude purge paquet1", "aptitude purge paquet2", "aptitude purge paquet3". Ceci se réaliserait par :
Ou alors je pourrais vouloir écrire "aptitude purge paquet1 paquet2 paquet3" car la commande aptitude supporte le passage de plusieurs arguments. Dans ce cas là je peux utiliser xargs :
En effet voici ce que fait xargs :
... et je peux passer à xargs une commande en paramètre à laquelle elle va passer le flux qu'elle a mis en ligne :
Dernier truc à savoir, une commande sépare dans un flux les éléments grâce aux espaces, mais aussi les retours chariots. Ainsi je peux écrire directement (et c'est la commande que j'utilise en pratique) :
J'espère que ça t'aura éclairé, mais si tu as besoin de précisions, n'hésite pas ;-)
En fait tu mets le doigts sur l'un des gros soucis de bash, que ce soit
- pour appeler une fonction shell à laquelle on passe plusieurs arguments (ce qui sépare les arguements... c'est le caractère espace, comme dans n'importe quelle commande shell !)
- pour passer un argument à un script ou à une fonction shell.
Variables $@ et $* (scripts et appels de fonctions shell)
C'est justement à cause de ce genre de considérations qu'on utilise par exemple $@ ou $*.
Exemple : ll.sh
#!/bin/sh echo "************" ls -l "$*" # va échouer echo "@@@@@@@@@@@@" ls -l "$@" # ok exit 0
Test :
mkdir "~/mon rep" "~/mon rep2" ./ll.sh ~/mon*
Séquence d'échappement
De manière générale, à moins d'explicitement échapper un espace, celui-ci est considéré comme le métacaractère de séparation.
Ainsi :
cd "mon répertoire" cd mon\ répertoire
... sont équivalentes. Par contre :
cd mon répertoire
... signifie qu'on passe un argument "mon" et "répertoire" (et donc là ça va planter).
xargs, pipe, for et opérateur $(...) '...'
En fait la principale difficulté en shell c'est de comprendre comment itérer sur un ensemble d'élément.
Alors avant tout je précise tout de suite que contrairement à ce que la mise en forme (stupide), ce ne sont pas des apostrophes mais des backquotes (alt gr 7) qu'ils faut utiliser quand je parle de l'opérateur '...'.
En bash l'opérateur $(...) se comporte comme l'opérateur '...', il permet d'évaluer le contenu de l'opérande en tant que commande et de la substituer par son résultat.
Exemple :
(mando@aldur) (~) $ seq 1 5 1 2 3 4 5 (mando@aldur) (~) $ for x in 'seq 1 5'; do echo "coucou" $x; done coucou 1 coucou 2 coucou 3 coucou 4 coucou 5
Note qu'en bash (un shell particulier qui fournit des opérateurs et des fonctions supplémentaires par rapport à un shell générique) on peut également écrire :
for x in $(seq 1 5); do echo "coucou" $x; done
Par la suite j'utiliserai $(...) pour éviter les ambiguïtés.
Si on utilise un opérateur capable de manipuler un flux sur /dev/stdin (l'entrée standard), comme grep, cut etc... on peut appliquer cette commande à chaque ligne reçu au niveau du flux.
Exemple :
dpkg -l | grep "^ii" | cut -d" " -f3
Le processus dpkg -l liste les paquets installés ou partiellement installés sous debian. Chaque paquets installés est précédé d'un tag (ii). Le nom du paquet apparaît derrière le deuxième caractère espace.
Ainsi :
(mando@aldur) (~) $ dpkg -l ... ii xterm 271-1 X terminal emulator rc xulrunner-1.9. 1.9.1.19-3 XUL + XPCOM application runner ii xulrunner-7.0 7.0.1-2 XUL + XPCOM application runner ii xz-utils 5.1.1alpha+201 XZ-format compression utilities ii zip 3.0-4 Archiver for .zip files ii zlib1g 1:1.2.3.4.dfsg compression library - runtime (mando@aldur) (~) $ dpkg -l | grep "^ii" ... ii xterm 271-1 X terminal emulator rc xulrunner-1.9. 1.9.1.19-3 XUL + XPCOM application runner ii xulrunner-7.0 7.0.1-2 XUL + XPCOM application runner ii xz-utils 5.1.1alpha+201 XZ-format compression utilities ii zip 3.0-4 Archiver for .zip files ii zlib1g 1:1.2.3.4.dfsg compression library - runtime (mando@aldur) (~) $ dpkg -l | grep "^ii" | cut -d" " -f3 ... xterm xulrunner-7.0 xz-utils zip zlib1g
De manière générale quand un traitement peut être réalisée par un pipe c'est toujours mieux (plus performant).
Ici supposons que je veuille supprimer tous les paquets "rc" (partiellement installés, sous debian ça veut dire que le paquets est supprimé, mais pas ses fichiers de configuration).
dpkg -l | grep "^rc" | cut -d" " -f3
... me renverra ces paquets. Maintenant je peux dire que je lance successivement "aptitude purge paquet1", "aptitude purge paquet2", "aptitude purge paquet3". Ceci se réaliserait par :
for paquet in $(dpkg -l | grep "^rc" | cut -d" " -f3); do aptitude purge $paquet, done
Ou alors je pourrais vouloir écrire "aptitude purge paquet1 paquet2 paquet3" car la commande aptitude supporte le passage de plusieurs arguments. Dans ce cas là je peux utiliser xargs :
dpkg -l | grep "^rc" | cut -d" " -f3 | xargs aptitude purge
En effet voici ce que fait xargs :
(mando@aldur) (~) $ dpkg -l | grep "^rc" | cut -d" " -f3 gimp-data libatkmm-1.6-1 libavformat52 libbabl-0.0-0 libcairomm-1.0-1 ... (mando@aldur) (~) $ dpkg -l | grep "^rc" | cut -d" " -f3 | xargs gimp-data libatkmm-1.6-1 libavformat52 libbabl-0.0-0 libcairomm-1.0-1 libcroco3 libdv4 libexif12 libgail18 libgconfmm-2.6-1c2 libgegl-0.0-0 libgimp2.0 libglibmm-2.4-1c2a libgraphite3...
... et je peux passer à xargs une commande en paramètre à laquelle elle va passer le flux qu'elle a mis en ligne :
dpkg -l | grep "^rc" | cut -d" " -f3 | xargs aptitude purge
Dernier truc à savoir, une commande sépare dans un flux les éléments grâce aux espaces, mais aussi les retours chariots. Ainsi je peux écrire directement (et c'est la commande que j'utilise en pratique) :
aptitude purge $(dpkg -l | grep "^rc" | cut -d" " -f3)
J'espère que ça t'aura éclairé, mais si tu as besoin de précisions, n'hésite pas ;-)
jisisv
Messages postés
3645
Date d'inscription
dimanche 18 mars 2001
Statut
Modérateur
Dernière intervention
15 janvier 2017
934
Modifié par jisisv le 13/10/2011 à 01:22
Modifié par jisisv le 13/10/2011 à 01:22
C'est normal., cpio prend sur l'entrée standard les noms des fichiers séparés par des newlines.
donc:
Enfin cpio est plutôt obsolète de nos jours.
Utilise plutôt tar (avec compression gzip, bzip2, xz éventuelle)
(à moins que tu ne doives résoudre un execice scolaire, les enseignants ayant une fâcheuse tendance à ne pas se tenir au courant)
Johan
Gates gave ^H sold you the windows.
GNU gave us the whole house.(Alexandrin)
donc:
johand@osiris: ~/tmp/cpio $ echo -ne "a\nb\nc" | cpio -o > out.cpio 1 bloc johand@osiris: ~/tmp/cpio $ echo -ne "a b c" | cpio -o > out.cpio cpio: a b c: échec de la fonction : stat: Aucun fichier ou dossier de ce type 1 bloc johand@osiris: ~/tmp/cpio $ touch "Un nom de fichier avec des espaces" johand@osiris: ~/tmp/cpio $ echo Un\ nom\ de\ fichier\ avec\ des\ espaces | cpio -o > brol.cpio 1 bloc johand@osiris: ~/tmp/cpio $ cpio -i --list < brol.cpio Un nom de fichier avec des espaces 1 bloc
Enfin cpio est plutôt obsolète de nos jours.
Utilise plutôt tar (avec compression gzip, bzip2, xz éventuelle)
(à moins que tu ne doives résoudre un execice scolaire, les enseignants ayant une fâcheuse tendance à ne pas se tenir au courant)
Johan
Gates gave ^H sold you the windows.
GNU gave us the whole house.(Alexandrin)
Merci pour vos réponses! Ça m'aide beaucoup, certaines commandes comme cpio attendent donc des arguments séparés par des sauts de ligne, et d'autres comme le for, une séparation que ce soit espace ou saut de ligne.
Je vais quand même en redemander puisque je peux:)
- Comment se fait il qu'un "ls" nous affiche un résultat avec des espaces pour séparateurs, alors qu'en fait elle les sépare bien avec des retours à la ligne ?? (visible en faisant ls | cat)
- (J'avais une autre question sur les différences entre l'éxécution dans un script bash et la ligne de commande "à la main" mais je viens de comprendre!)
re merci d'avance.
ps: en fait je n'utilise ni cpio ni tar mais pax, qui permet de faire plus de choses et génère du tar. Il se comporte comme cpio donc j'ai mis cpio dans mon exemple car il est plus connu je pense.
Je vais quand même en redemander puisque je peux:)
- Comment se fait il qu'un "ls" nous affiche un résultat avec des espaces pour séparateurs, alors qu'en fait elle les sépare bien avec des retours à la ligne ?? (visible en faisant ls | cat)
- (J'avais une autre question sur les différences entre l'éxécution dans un script bash et la ligne de commande "à la main" mais je viens de comprendre!)
re merci d'avance.
ps: en fait je n'utilise ni cpio ni tar mais pax, qui permet de faire plus de choses et génère du tar. Il se comporte comme cpio donc j'ai mis cpio dans mon exemple car il est plus connu je pense.
mamiemando
Messages postés
33357
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
13 novembre 2024
7 805
13 oct. 2011 à 19:53
13 oct. 2011 à 19:53
- Comment se fait il qu'un "ls" nous affiche un résultat avec des espaces pour séparateurs, alors qu'en fait elle les sépare bien avec des retours à la ligne ?? (visible en faisant ls | cat)
cat prend un paramètre un fichier. Il le lit "ligne par ligne" et affiche successivement chaque ligne. Ce fichier peut être /dev/stdin (la saisie au clavier typiquement). C'est sur ça que repose le fait d'utiliser | cat.
Fais un ls > toto et regarde le contenu de toto. Tu verras que dans toto tu as bien un fichier par ligne. C'est ce flux qui est manipulé transmis par |. Je ne peux pas te dire exactement comment cette remise en forme est faite mais dans la même veine, la commande mysql est capable de remettre en forme la sortie d'une requête mysql, et quand tu rediriges sont résultat dans un fichier le résultat est différent. Bref ce que tu vois dans la console n'est pas ce qui est réellement écrit dans le flux.
ps: en fait je n'utilise ni cpio ni tar mais pax, qui permet de faire plus de choses et génère du tar. Il se comporte comme cpio donc j'ai mis cpio dans mon exemple car il est plus connu je pense.
Attention car pax n'est pas une commande installée forcément sur tous les systèmes, tandis que tar est généralement présent. Utiliser pax restreint le domaine d'utilisation de ton script. Si tu peux utiliser tar c'est mieux par exemple.
Bonne chance
cat prend un paramètre un fichier. Il le lit "ligne par ligne" et affiche successivement chaque ligne. Ce fichier peut être /dev/stdin (la saisie au clavier typiquement). C'est sur ça que repose le fait d'utiliser | cat.
Fais un ls > toto et regarde le contenu de toto. Tu verras que dans toto tu as bien un fichier par ligne. C'est ce flux qui est manipulé transmis par |. Je ne peux pas te dire exactement comment cette remise en forme est faite mais dans la même veine, la commande mysql est capable de remettre en forme la sortie d'une requête mysql, et quand tu rediriges sont résultat dans un fichier le résultat est différent. Bref ce que tu vois dans la console n'est pas ce qui est réellement écrit dans le flux.
ps: en fait je n'utilise ni cpio ni tar mais pax, qui permet de faire plus de choses et génère du tar. Il se comporte comme cpio donc j'ai mis cpio dans mon exemple car il est plus connu je pense.
Attention car pax n'est pas une commande installée forcément sur tous les systèmes, tandis que tar est généralement présent. Utiliser pax restreint le domaine d'utilisation de ton script. Si tu peux utiliser tar c'est mieux par exemple.
Bonne chance
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
merci
Je l'accepte alors meme si je sais pas pourquoi ça se comporte comme ça.J'en tiendrai compte et je pourrai me débrouiller
Je l'accepte alors meme si je sais pas pourquoi ça se comporte comme ça.J'en tiendrai compte et je pourrai me débrouiller
mamiemando
Messages postés
33357
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
13 novembre 2024
7 805
16 oct. 2011 à 22:39
16 oct. 2011 à 22:39
Du coup est-ce que ton problème est résolu ?