web-dev-qa-db-fra.com

Pourquoi l'écho ignore-t-il mes caractères de citation?

La commande echo n'inclut pas le texte complet que je lui donne. Par exemple, si je le fais:

   $ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '

Il génère:

echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk {print } `

Les guillemets simples (') Que j'avais dans ma commande echo ne sont pas inclus. Si je passe aux guillemets doubles:

$ echo " echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `  "

echo ne produit rien du tout! Pourquoi oublie-t-il les guillemets simples dans le premier exemple et ne produit-il rien dans le second? Comment puis-je le faire sortir exactement ce que j'ai tapé?

24
Diana

Votre Shell interprète les guillemets, à la fois ' et ", avant même d'arriver à echo. Je mets généralement des guillemets autour de mon argument pour faire écho même s'ils ne sont pas nécessaires; par exemple:

$ echo "Hello world"
Hello world

Ainsi, dans votre premier exemple, si vous souhaitez inclure des guillemets littéraux dans votre sortie, vous devez soit les échapper:

$ echo \'Hello world\'
'Hello world'

Ou ils doivent déjà être utilisés dans un argument cité (mais il ne peut pas s'agir du même type de citation, ou vous devrez de toute façon y échapper):

$ echo "'Hello world'"
'Hello world'

$ echo '"Hello world"'
"Hello world"

Dans votre deuxième exemple, vous exécutez une substitution de commande au milieu de la chaîne:

grep  $ARG  /var/tmp/setfile  | awk {print $2}

Les choses qui commencent par $ sont également gérés spécialement par le Shell - il les traite comme des variables et les remplace par leurs valeurs. Étant donné que probablement aucune de ces variables n'est définie dans votre shell, il s'exécute simplement

grep /var/tmp/setfile | awk {print}

Puisque grep ne voit qu'un seul argument, il suppose que cet argument est le modèle que vous recherchez et que l'endroit où il doit lire les données est stdin, il bloque donc l'attente de l'entrée. C'est pourquoi votre deuxième commande semble se bloquer.

Cela ne se produira pas si vous citez l'argument (c'est pourquoi votre premier exemple a presque fonctionné), c'est donc une façon d'obtenir la sortie que vous souhaitez:

echo \'' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `    '\'

Vous pouvez également le mettre entre guillemets, mais vous devrez ensuite échapper au $s pour que le Shell ne les résout pas en tant que variables, et les raccourcis pour que le Shell n'exécute pas immédiatement la substitution de commande:

echo "' echo PARAM=\`  grep  \$ARG  /var/tmp/setfile  | awk '{print \$2}' \`    '"
33
Michael Mrozek

Je ne vais pas entrer dans les détails pour expliquer pourquoi vos tentatives se comportent comme elles le font, car réponse de Michael Mrozek couvre bien cela. En un mot, tout entre guillemets simples ('…') est interprété littéralement (et en particulier le premier ' marque la fin de la chaîne littérale), tandis que ` et $ conservent leur signification particulière entre "…".

Il n'y a pas de guillemets dans les guillemets simples, vous ne pouvez donc pas mettre de guillemet simple dans une chaîne entre guillemets simples. Il existe cependant un idiome qui lui ressemble:

echo 'foo'\''bar'

Cela imprime foo'bar, car l'argument de echo est constitué de la chaîne de trois caractères entre guillemets foo, concaténée avec le caractère unique ' (obtenu en protégeant le caractère ' de sa signification spéciale à travers le précédent \), concaténé avec la chaîne de trois caractères entre guillemets bar. Donc, bien que ce ne soit pas tout à fait ce qui se passe dans les coulisses, vous pouvez penser à '\'' comme moyen d'inclure un guillemet simple dans une chaîne entre guillemets simples.

Si vous souhaitez imprimer des chaînes multilignes complexes, un meilleur outil est le ici document . Un document ici se compose des deux caractères << suivi d'un marqueur tel que <<EOF, puis quelques lignes de texte, puis le marqueur de fin seul sur sa ligne. Si le marqueur est cité de quelque façon que ce soit ('EOF' ou "EOF" ou \EOF ou 'E "" OF' ou ...), le texte est alors interprété littéralement (comme à l'intérieur de guillemets simples, sauf que même ' est un caractère ordinaire). Si le marqueur n'est pas du tout cité, le texte est interprété comme dans une chaîne entre guillemets doubles, avec $\` conservant leur statut spécial (mais " et les sauts de ligne sont interprétés littéralement).

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF

D'accord, cela fonctionnera: -

echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '

Le tester ici: -

[asmith@yagi ~]$ echo ' echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' `    '
 echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ` 
1
Andy Smith

suggestion de Gilles d'utiliser un document ici est vraiment sympa, et fonctionne même dans des langages de script comme Perl. Comme exemple spécifique basé sur sa réponse à la question du PO,

use strict;
my $cmd = q#cat <<'EOF' # . "\n" .
          q#echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `# . "\n" .
          q#EOF# ;
open (my $OUTPUT, "$cmd |");
print $_ while <$OUTPUT>;
close $OUTPUT;

Certes, cet exemple est un peu artificiel, mais j'ai trouvé que c'était une technique utile pour, disons, envoyer des instructions SQL à psql (au lieu de cat).

(Notez que tout caractère non alphanumérique non espace peut être utilisé à la place du # dans la construction générique citant ci-dessus, mais le hachage semble bien ressortir pour cet exemple, et n'est pas traité comme un commentaire.)

0
Randall