97 votes

Bash script pour recevoir et repasser les paramètres cités

J'essaie de faire en sorte que les paramètres cités d'un script bash soient reçus en toute sécurité par un script imbriqué. Une idée ?

test.sh

#!/bin/bash
echo $*
bash myecho.sh $*

myecho.sh

#!/bin/bash
 echo $1
 echo $2
 echo $3
 echo $4

Échantillon :

bash test.sh aaa bbb '"ccc ddd"'

Résultat :

aaa bbb "ccc ddd"
aaa
bbb
"ccc
ddd"

Résultat souhaité

aaa bbb "ccc ddd"
aaa
bbb
ccc ddd

141voto

Dave Dopson Points 16690

Vous voulez utiliser "$@" (le dollar entre guillemets) pour passer des paramètres à un indice. Comme suit : ....

ls-color.sh:

#!/bin/bash
/bin/ls --color=auto "$@"    # passes though all CLI-args to 'ls'

Quant à savoir pourquoi.....

De la Page de manuel de Bash :

$* -- S'étend aux paramètres positionnels, en commençant par un. Lorsque l'expansion se produit entre guillemets doubles, elle se développe en un seul mot avec la valeur de chaque paramètre séparée par le premier caractère de la variable spéciale de la variable spéciale IFS. C'est-à-dire "$*" est équivalent à "$1c$2c..." où c est le premier caractère de la valeur de l'IFS. de la variable IFS. Si IFS n'est pas défini, les paramètres sont séparés par des espaces. Si IFS est nul, les paramètres sont joints sans séparateurs intermédiaires.

$@ -- S'étend aux paramètres positionnels, en commençant par un. Lorsque l'expansion se fait entre guillemets, chaque paramètre se développe en un mot mot séparé. C'est-à-dire, "$@" est équivalent à "$1" "$2" ... Si le l'expansion entre guillemets se produit à l'intérieur d'un mot, l'expansion du premier paramètre est joint au début du mot d'origine, et l'expansion du d'origine, et l'expansion du dernier paramètre est jointe à la dernière partie du mot d'origine. partie du mot d'origine. Lorsqu'il n'y a pas de paramètres positionnels, "$@" y $@ se réduisent à néant (c'est-à-dire qu'ils sont supprimés).


Mise en place de quelques scripts de démonstration ...

echo 'echo -e "\$1=$1\n\$2=$2\n\$3=$3\n\$4=$4"' > echo-params.sh
echo './echo-params.sh $*' > dollar-star.sh
echo './echo-params.sh $@' > dollar-at.sh
echo './echo-params.sh "$*"' > quoted-dollar-star.sh
echo './echo-params.sh "$@"' > quoted-dollar-at.sh
chmod +x *.sh

"$@" - quoted-dollar-at est un transformation identitaire pour repasser les args à un sous-shell (~99% du temps, c'est ce que vous vouliez faire) :

./quoted-dollar-at.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa
  # $2=            
  # $3= 'cc cc'
  # $4= "ddd ddd"

"$*" - cité-étoile casse les args en une seule chaîne (~1% du temps vous voulez réellement ce comportement, par exemple dans une conditionnelle : if [[ -z "$*" ]]; then ... ) :

./quoted-dollar-star.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa  'cc cc' "ddd ddd"   
  # $2=                     
  # $3=             
  # $4=

$* / $@ - sans guillemets, les deux formulaires suppriment un niveau de guillemets et interprètent les espaces des chaînes sous-jacentes mais ignorent les caractères de guillemets (c'est presque toujours une erreur) :

./dollar-star.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa
  # $2= 'cc                  
  # $3= cc'
  # $4= "ddd

./dollar-at.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa
  # $2= 'cc
  # $3= cc'
  # $4= "ddd

Si vous voulez vous amuser, vous pouvez utiliser "$@" pour imbriquer les choses aussi profondément que vous le souhaitez, en poussant et en retirant des éléments de la pile d'args si vous le souhaitez.

function identity() {
  "$@"
}
set -x
identity identity identity identity identity echo Hello \"World\"
# + identity identity identity identity identity echo Hello '"World"'
# + identity identity identity identity echo Hello '"World"'
# + identity identity identity echo Hello '"World"'
# + identity identity echo Hello '"World"'
# + identity echo Hello '"World"'
# + echo Hello '"World"'
# Hello "World"

69voto

pixelbeat Points 12073
#!/bin/bash
echo $*
bash myecho.sh "$@"

Notez que la construction "$@" n'est pas spécifique à bash et devrait fonctionner avec n'importe quel shell POSIX (c'est le cas avec dash au moins). Notez également qu'étant donné la sortie que vous voulez, vous n'avez pas du tout besoin du niveau supplémentaire de citation. Par exemple, il suffit d'appeler le script ci-dessus comme suit :

./test.sh 1 2 "3 4"

2voto

Geoff Nixon Points 146

Les réponses ci-dessus sont vraiment, vraiment bien .
Mon seul point de clarification serait que "$*" est probablement ce que vous voulez plus comme ~0.2% du temps.

J'écris beaucoup de l'interpréteur de commandes scripts, et ce n'est que maintenant que je rencontre le premier cas où cela est vrai.
Observez la différence de comportement entre ces deux scripts.
Essayez avec quelque chose comme : (Bien que le dernier devra toujours être cité pour &, %, etc., et ? dans les poissons).

./script wherewith whence wherefore
./script wherewith "from whence" wherefore
./script wherewith https://whence.whence/~whence?whence=whence wherefore

Cela ne fonctionne pas de manière fiable, parce qu'il est trop étendu (ligne 3) :

#!/usr/bin/env sh
printf(){ command printf "$@"; }
print(){ [ x"$@" != x ] && [ -c /dev/stdin ] && printf "$@\n" || printf "$@"; }
with=$1; for where; do :; done; what="$(print "$@" | sed "s|^$with \(.*\) $where$|\1|")"
print "$with $what $where"

Mais ça, oui :

#!/usr/bin/env sh
printf(){ command printf "$@"; }
print(){ [ x"$*" != x ] && [ -c /dev/stdin ] && printf "$@\n" || printf "$@"; }
with=$1; for where; do :; done; what="$(print "$*" | sed "s|^$with \(.*\) $where$|\1|")"
print "$with $what $where"  

Avec des commentaires (parce que c'est un peu du charabia sans eux) :

#!/usr/bin/env sh # This script is trying *absurdy hard* to be portable.
# Works in (at least): sh, bash, dash, zsh, ksh, mksh, pdksh...
# Splits command line into three variables, first, last, and everything else.  

# Something you might do because builtins are buggy/unreliable.
printf(){
   command printf "$@"
  }

# Same reason (i.e. to avoid 'echo'), and to avoid echoing newlines to stdout.
print(){
  [ x"$*" != x ] && [ -c /dev/stdin ] && printf "$@\n" || printf "$@"
 }

# Maybe a subcommand.
with=$1

# Maybe a directory. (Neat little trick to grab the last argument to a script or function.)
for where; do :; done

# Maybe a URI or another path.
what="$(print "$*" | sed "s|^$with \(.*\) $where$|\1|")"

print "" # Nothing.
print "$with"; print "$what"; print "$where"

# Or, portably create a tempfile.
temporary="/tmp/$(od -XN4 -An /dev/random | sed 's| ||g')"    
print "$with $what $where" > $temporary
cat $temporary
rm $temporary

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