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