Comment passer les arguments du script bash à un sous-ensemble

J'ai un script wrapper qui fait du travail puis passe les paramètres d'origine sur un autre outil:

#!/bin/bash # ... other_tool -a -b "$@" 

Cela fonctionne bien, sauf si le "autre outil" est exécuté en sous-cas:

 #!/bin/bash # ... bash -c "other_tool -a -b $@" 

Si j'appelle mon script wrapper comme ceci:

 wrapper.sh -x "blah blup" 

Alors, seul le premier argument original (-x) est remis à "other_tool". En réalité, je ne crée pas un sous-ensemble, mais je passe les arguments originaux à un shell sur un téléphone Android, ce qui ne devrait pas faire de différence:

 #!/bin/bash # ... adb sh -c "other_tool -a -b $@" 

La commande printf de Bash comporte une fonctionnalité qui cite / s'échappe, quelle que soit la chaîne, tant que le parent et le subshell sont en fait bash, cela devrait fonctionner:

 #!/bin/bash quoted_args="$(printf " %q" "$@")" # Note: this will have a leading space before the first arg # echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing bash -c "other_tool -a -b$quoted_args" 

Notez que vous pouvez également le faire en une seule ligne: bash -c "other_tool -a -b$(printf " %q" "$@")"

Changer $@ à $* . J'ai fait un petit test local et cela fonctionne dans mon cas.

 #!/bin/sh bash -c "echo $*" bash -c "echo $@" 

Enregistrer comme test.sh et le rendre exécutable donne

 $ ./test.sh foo bar foo bar foo 

Il existe une différence subtile entre $* et $@ , comme vous pouvez le constater. Voir par exemple http://ss64.com/bash/syntax-parameters.html


Pour la question de suivi dans les commentaires: vous devez échapper, par exemple, à l'espace blanc "deux fois" pour passer une chaîne avec un séparateur comme argument combiné, par exemple avec test.sh modifié à un wrapper wc :

 #!/bin/sh bash -c "wc $*" 

Cela marche:

 $ touch test\ file $ ./test.sh -l "test\ file" 0 test file 

mais:

 $ ./test.sh -l "test file" wc: test: No such file or directory wc: file: No such file or directory 0 total 

Aucune solution ne fonctionne bien. Il suffit de passer x / \ \ "b \" / aaaaa / \ 'xxx \ aaaa \' / zz \ ​​"offf \" comme paramètre et ils échouent.

Voici une enveloppe simple qui gère chaque cas. Notez comment il échappe à chaque argument deux fois.

 #!/usr/bin/env bash declare -a ARGS COUNT=$# for ((INDEX=0; INDEX<COUNT; ++INDEX)) do ARG="$(printf "%q" "$1")" ARGS[INDEX]="$(printf "%q" "$ARG")" shift done ls -l ${ARGS[*]} 

Ca manque parce que vous contraignez un tableau (les paramètres de position) dans une chaîne. "$@" Est magique car il vous donne chaque paramètre séparé comme une chaîne correctement cité. L'ajout de texte supplémentaire casse la magie: "blah $@" est juste une seule chaîne.

Cela peut vous rapprocher:

 cmd="other_tool -a -b" for parm in "$@"; do cmd+=" '$parm'"; done adb sh -c "$cmd" 

Bien sûr, tout paramètre contenant une seule citation entraînera des problèmes.

Ok, plus d'explications:

 $ cat /tmp/test.sh #! /bin/bash echo '$@='"$@" set -v # "debug" sh -c 'echo $@' "$@" # gremlins steal the first option sh -c 'echo $@' -- "$@" # We defend the option with two knifes $ bash -e /tmp/test.sh first second third $@=first second third sh -c 'echo $@' "$@" # gremlins steal the first option second third sh -c 'echo $@' -- "$@" # We defend the option with two knifes first second third 
 #! /bin/bash sh -c 'other_tool -a -b "$@"' -- "$@"