1100 votes

Comment parcourir les arguments au script bash

J’ai une commande complexe que je voudrais faire un script shell/bash de. Je peux l’écrire en termes de `` facilement :

Je veux pouvoir passer plusieurs noms d’entrée dans le script - ce qui est la bonne façon de le faire ? Bien sûr, je tiens à gérer les noms de fichiers avec des espaces en eux.

1813voto

Robert Gamble Points 41984

Utilisation `` pour représenter tous les arguments :

Cela va itérer sur chaque argument et imprimez-le sur une ligne distincte. $@ se comporte comme $* sauf qu’une fois cité les arguments sont cassés vers le haut correctement s’il existe des espaces en eux :

264voto

Jonathan Leffler Points 299946

Réécriture d'un désormais supprimé réponse par VonC

Robert Gamble succincte réponse traite directement de la question. Ce un amplifie sur certaines questions avec des noms de fichiers contenant des espaces.

Voir aussi: ${1:+"$@"} dans /bin/sh

Thèse fondamentale: "$@" est correct, et $* (non cotées) est presque toujours tort. C'est parce qu' "$@" fonctionne très bien lorsque les arguments contenant des espaces, et fonctionne de la même comme $* quand ils ne le font pas. Dans certaines circonstances, "$*" est OK aussi, mais "$@" généralement (mais pas toujours) fonctionne dans les mêmes endroits. Non cotées, $@ et $* sont équivalentes (et presque toujours tort).

[Ajouté: Alors, quelle est la différence entre $*, $@, "$*", et "$@"? Ils sont tous liés à "tous les arguments à la coquille", mais ils font des choses différentes. Lorsque les sociétés non cotées, $* et $@ faire la même chose. Ils traitent chaque " mot " (séquence de non-blanc) comme un argument séparé. La cité formes sont assez différentes, même si: "$*" traite la liste des arguments comme une seule chaîne séparée par des espaces, alors que "$@" traite les arguments presque exactement comme ils l'étaient lorsque spécifié sur la ligne de commande. Voir plus d'informations ci-dessous, après l'introduction de la (non-standard) commande al.
]

Secondaire thèse: si vous avez besoin de traiter les arguments contenant des espaces, puis de les transmettre à d'autres commandes, vous avez parfois besoin non standard des outils pour les aider.

Exemple:

    $ mkdir "my dir" anotherdir
    $ ls
    anotherdir      my dir
    $ cp /dev/null "my dir/my file"
    $ cp /dev/null "anotherdir/myfile"
    $ ls -Fltr
    total 0
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir/
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir/
    $ ls -Fltr *
    my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ ls -Fltr "./my dir" "./anotherdir"
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ var='"./my dir" "./anotherdir"' && echo $var
    "./my dir" "./anotherdir"
    $ ls -Fltr $var
    ls: "./anotherdir": No such file or directory
    ls: "./my: No such file or directory
    ls: dir": No such file or directory
    $

Pourquoi ne pas travailler? Cela ne fonctionne pas car le shell les processus de devis avant d'augmenter les variables. Donc, pour obtenir la coquille de payer l'attention sur les citations intégrées en $var, vous devez utiliser eval:

    $ eval ls -Fltr $var
    ./my dir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 my file

    ./anotherdir:
    total 0
    -rw-r--r--   1 jleffler  staff  0 Nov  1 14:55 myfile
    $ 

Cela devient vraiment difficile quand vous avez des noms de fichier telles que "He said, "Don't do this!"" (avec des guillemets et des apostrophes doubles et des espaces).

    $ cp /dev/null "He said, \"Don't do this!\""
    $ ls
    He said, "Don't do this!"       anotherdir                      my dir
    $ ls -l
    total 0
    -rw-r--r--   1 jleffler  staff    0 Nov  1 15:54 He said, "Don't do this!"
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 anotherdir
    drwxr-xr-x   3 jleffler  staff  102 Nov  1 14:55 my dir
    $ 

Les coques (toutes) ne rendent pas particulièrement facile à manipuler trucs, donc (curieusement) de nombreux programmes Unix ne pas faire un bon travail de la manipulation. Sur Unix, un nom de fichier (un seul composant) peut contenir n'importe quel caractère, à l'exception slash et NUL '\0'. Cependant, les coquilles d'encourager fortement, sans espaces ou des retours à la ligne ou des tabulations n'importe où dans un les noms de chemin. C'est aussi pourquoi la norme Unix, les noms de fichiers ne contiennent pas d'espaces, etc.

Lorsque vous traitez avec des noms de fichier qui peut contenir des espaces et d'autres gênants caractères, vous devez être extrêmement prudent, et je l'ai trouvé il y a longtemps que j'ai besoin d'un programme qui n'est pas standard sous Unix. J'appelle ça de l' escape (version 1.1 a été daté 1989-08-23T16:01:45Z).

Voici un exemple d' escape - avec le SCC système de contrôle. Il s'agit d'une couverture script qui effectue à la fois une delta (pense que le check-in) et un get (pensez check-out). Divers arguments, particulièrement -y (la raison pourquoi vous avez fait le changement) permettrait de contenir d'espaces et retours à la ligne. Notez que le script date de 1992, de sorte qu'il utilise en arrière-tiques au lieu de $(cmd ...) de la notation et de ne pas utiliser #!/bin/sh sur la première ligne.

:   "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
#   Delta and get files
#   Uses escape to allow for all weird combinations of quotes in arguments

case `basename $0 .sh` in
deledit)    eflag="-e";;
esac

sflag="-s"
for arg in "$@"
do
    case "$arg" in
    -r*)    gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    -e)     gargs="$gargs `escape \"$arg\"`"
            sflag=""
            eflag=""
            ;;
    -*)     dargs="$dargs `escape \"$arg\"`"
            ;;
    *)      gargs="$gargs `escape \"$arg\"`"
            dargs="$dargs `escape \"$arg\"`"
            ;;
    esac
done

eval delta "$dargs" && eval get $eflag $sflag "$gargs"

(Je n'aurais probablement pas utiliser échapper tout à fait si bien ces jours - ci- il est ne sont pas nécessaires avec l' -e argument, par exemple - mais dans l'ensemble, c'est l'un de mes plus simple de scripts à l'aide de escape.)

L' escape programme il suffit de sorties de ses arguments, plutôt que d' echo n', mais il assure que les arguments sont protégés pour une utilisation avec eval (un niveau d' eval; j'ai un programme qui n'a remote shell d'exécution, et que le besoin d'échapper à la sortie de l' escape).

    $ escape $var
    '"./my' 'dir"' '"./anotherdir"'
    $ escape "$var"
    '"./my dir" "./anotherdir"'
    $ escape x y z
    x y z
    $ 

J'ai un autre programme appelé" al qui dresse la liste de ses arguments un par ligne (et c'est encore plus ancien: version 1.1 du 1987-01-27T14:35:49). Il est le plus utile lors du débogage de scripts, comme il peut être branché dans une ligne de commande pour voir quels sont les arguments passés à la commande.

    $ echo "$var"
    "./my dir" "./anotherdir"
    $ al $var
    "./my
    dir"
    "./anotherdir"
    $ al "$var"
    "./my dir" "./anotherdir"
    $

[Ajouté: Et maintenant, pour montrer la différence entre les différents "$@" de notations, ici est un exemple de plus:

$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh     *      */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$

Notez que rien ne préserve l'origine des blancs entre l' * et */* sur la ligne de commande. Aussi, notez que vous pouvez changer les "arguments de ligne de commande" dans le shell en utilisant:

set -- -new -opt and "arg with space"

Ce jeux de 4 options, '-new', '-opt', 'and"et"arg with space'.
]

Hmm, c'est tout à fait une longue réponse - peut-être l'exégèse est le meilleur terme. Le code Source pour escape disponible sur demande (e-mail à firstname dot nom at gmail dot com). Le code source pour al est très simple:

#include <stdio.h>
int main(int argc, char **argv)
{
    while (*++argv != 0)
        puts(*argv);
    return(0);
}

C'est tout. C'est équivalent à l' test.sh script que Robert Gamble a montré, et peut être écrite comme une fonction shell (mais shell fonctions n'existent pas dans la version locale de la Bourne shell lorsque j'ai d'abord écrit al).

152voto

Alok Singhal Points 33073

Notez que Robert réponse est bonne, et il fonctionne en sh . Vous pouvez (de façon portable) simplifier encore davantage:

for i in "$@"

est équivalent à:

for i

I. e., vous n'avez pas besoin de quoi que ce soit!

Les tests ($ est l'invite de commande):

$ set a b "spaces here" d
$ for i; do echo $i; done
a
b
spaces here
d
$ for i in "$@"; do echo $i; done
a
b
spaces here
d

J'ai d'abord lu à ce sujet dans l' Environnement Unix par Kernighan et le Brochet.

En bash, help for documents:

for NAME [in WORDS ... ;] do COMMANDS; done

Si 'in WORDS ...;' n'est pas présent, alors 'in "$@"' est assumé.

81voto

nuoritoveri Points 488

Pour les cas simples, vous pouvez également utiliser . Il traite la liste d’arguments comme une file d’attente, chaque lève le premier argument, le nombre de chaque argument qui est laissé est décrémenté.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X