110 votes

Comment conserver les citations dans les arguments de Bash ?

J'ai un Bash script où je veux garder les guillemets dans les arguments passés.

Ejemplo:

./test.sh this is "some test"

je veux ensuite utiliser ces arguments, et les réutiliser, en incluant des guillemets autour de toute la liste des arguments.

J'ai essayé d'utiliser \"$@\" mais cela supprime les guillemets à l'intérieur de la liste.

Comment puis-je y parvenir ?

126voto

Chris Dodd Points 39013

En utilisant "$@" substituera les arguments sous forme de liste, sans les re-séparer sur les espaces (ils ont été séparés une fois lorsque le shell script a été invoqué), ce qui est généralement exactement ce que vous voulez si vous voulez juste repasser les arguments à un autre programme.

Notez que c'est une forme spéciale et qu'elle n'est reconnue comme telle que si elle apparaît exactement de cette façon. Si vous ajoutez autre chose entre les guillemets, le résultat sera combiné en un seul argument.

Qu'essayez-vous de faire et en quoi cela ne fonctionne-t-il pas ?

58voto

Tom Hale Points 5950

Il y a deux façons sûres de le faire :

1. Expansion des paramètres de la coquille : ${variable@Q}:

Lorsque l'on développe une variable via ${variable@Q} :

L'expansion est une chaîne de caractères qui est la valeur du paramètre cité dans un format qui peut être réutilisé comme entrée.

Ejemplo:

$ expand-q() { for i; do echo ${i@Q}; done; }  # Same as for `i in "$@"`...
$ expand-q word "two words" 'new
> line' "single'quote" 'double"quote'
word
'two words'
$'new\nline'
'single'\''quote'
'double"quote'

2. printf %q "$quote-me"

printf prend en charge la citation en interne. Le site l'entrée du manuel pour printf dit :

%q Permet à printf de sortir l'argument correspondant dans un format qui peut être réutilisé comme entrée du shell.

Ejemplo:

$ cat test.sh 
#!/bin/bash
printf "%q\n" "$@"
$ 
$ ./test.sh this is "some test" 'new                                                                                                              
>line' "single'quote" 'double"quote'
this
is
some\ test
$'new\nline'
single\'quote
double\"quote
$

Notez que la deuxième méthode est un peu plus propre si vous affichez le texte cité à un humain.

En rapport : Pour bash, POSIX sh et zsh : Citer une chaîne de caractères avec des guillemets simples plutôt qu'avec des barres obliques inversées.

47voto

unhammer Points 646

La réponse de Yuku ne fonctionne que si vous êtes le seul utilisateur de votre script, tandis que celle de Dennis Williamson est excellente si vous êtes principalement intéressé par l'impression des chaînes de caractères, et que vous vous attendez à ce qu'elles n'aient pas de guillemets.

Voici une version qui peut être utilisée si vous voulez passer tous les arguments comme une seule grande chaîne de caractères entre guillemets à la fonction -c paramètre de bash o su :

#!/bin/bash
C=''
for i in "$@"; do 
    i="${i//\\/\\\\}"
    C="$C \"${i//\"/\\\"}\""
done
bash -c "$C"

Así que, tous les arguments sont entourés d'un guillemet (inoffensif s'il n'était pas là avant, dans ce but), mais nous échappons aussi à tout échappement et ensuite à tout guillemet qui était déjà dans un argument (la syntaxe ${var//from/to} fait une substitution globale des substrats).

Vous pourriez bien sûr ne citer que les choses qui contiennent déjà des espaces, mais cela n'aura pas d'importance ici. Une utilité d'un script comme celui-ci est d'être capable d'avoir un certain ensemble prédéfini de variables d'environnement (ou, avec su, de lancer des choses en tant qu'un certain utilisateur, sans ce désordre de double-citer tout).


Mise à jour : J'ai récemment eu une raison de faire ceci d'une manière POSIX avec un minimum de bifurcation, ce qui a conduit à ce script (le dernier printf ici sort la ligne de commande utilisée pour invoquer le script, que vous devriez être capable de copier-coller afin de l'invoquer avec des arguments équivalents) :

#!/bin/sh
C=''
for i in "$@"; do
    case "$i" in
        *\'*)
            i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"`
            ;;
        *) : ;;
    esac
    C="$C '$i'"
done
printf "$0%s\n" "$C"

Je suis passé à '' puisque les shells interprètent aussi des choses comme $ y !! en "" -Citations.

35voto

Dennis Williamson Points 105818

Si l'on peut supposer qu'un argument qui contient un espace blanc doit avoir été (et doit être) cité, alors vous pouvez les ajouter comme ceci :

#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
    if [[ $i =~ $whitespace ]]
    then
        i=\"$i\"
    fi
    echo "$i"
done

Voici un exemple d'exécution :

$ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx'
abc
def
"ghi jkl"
"mno    pqr"
"stu
vwx"

Vous pouvez également insérer les tabulations littérales et les sauts de ligne en utilisant Ctrl - V Tab y Ctrl - V Ctrl - J entre guillemets doubles ou simples, au lieu d'utiliser des échappatoires à l'intérieur de $'...' .

Une note sur en insérant en Bash : Si vous utilisez des liaisons de touches Vi ( set -o vi ) en Bash (Emacs est la version par défaut - set -o emacs ), vous devrez être dans insérer mode afin d'insérer des caractères . En mode Emacs, vous êtes toujours en mode insertion.

18voto

isarandi Points 628

J'en avais besoin pour transmettre tous les arguments à un autre interprète. Ce qui s'est avéré correct pour moi est :

bash -c "$(printf ' %q' "$@")"

Exemple (lorsqu'il est nommé forward.sh) :

$ ./forward.sh echo "3 4"
3 4
$ ./forward.sh bash -c "bash -c 'echo 3'"
3

(Bien sûr, le script réel que j'utilise est plus complexe, impliquant dans mon cas nohup et des redirections, etc. mais c'est la partie essentielle).

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