Por que o echo está ignorando meus caracteres de aspas?

19

O comando echo não está incluindo o texto completo que eu forneci. Por exemplo, se eu fizer:

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

Produz:

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

As aspas simples ( ' ) que eu tinha no meu comando echo não estão incluídas. Se eu alternar entre aspas duplas:

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

echo não produz nada! Por que está deixando de lado as aspas simples no primeiro exemplo e produzindo nada no segundo? Como obtenho para produzir exatamente o que eu digitei?

    
por Michael Mrozek 15.11.2011 / 17:20

5 respostas

27

Seu shell está interpretando as cotações, ' e " , antes mesmo de chegarem a echo . Eu geralmente apenas coloco aspas duplas em torno do meu argumento para ecoar, mesmo que sejam desnecessárias; por exemplo:

$ echo "Hello world"
Hello world

Assim, no seu primeiro exemplo, se você quiser incluir marcas de aspas literais em sua saída, elas precisam ser escapadas:

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

Ou eles precisam ser usados em um argumento já citado (mas não pode ser o mesmo tipo de citação, ou você precisará escapar dele de qualquer forma):

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

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

No seu segundo exemplo, você está executando uma substituição de comando no meio da string:

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

As coisas que começam com $ também são tratadas especialmente pelo shell - ele as trata como variáveis e as substitui por seus valores. Como provavelmente nenhuma dessas variáveis está configurada no seu shell, ela simplesmente roda

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

Como grep só vê um argumento, ele assume que o argumento é o padrão que você está procurando e que o local para o qual ele deve ler dados é stdin, portanto ele bloqueia a espera pela entrada. É por isso que seu segundo comando parece apenas travar.

Isso não acontecerá se você citar o argumento com uma só letra (e é por isso que seu primeiro exemplo quase funcionou), portanto, essa é uma maneira de obter a saída desejada:

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

Você também pode citar duas vezes, mas você precisará escapar do $ s para que o shell não as resolva como variáveis, e os backticks para que o shell não execute a substituição de comando imediatamente :

echo "' echo PARAM=\'  grep  \$ARG  /var/tmp/setfile  | awk '{print \}' \'    '"
    
por 15.11.2011 / 19:24
6

Eu não vou entrar em muitos detalhes sobre o porquê de suas tentativas se comportarem da mesma maneira, porque A resposta de Michael Mrozek fala bem disso. Em suma, tudo entre aspas simples ( '…' ) é interpretado literalmente (e, em particular, o primeiro ' marca o final da string literal), enquanto ' e $ mantêm seu significado especial entre "…" .

Não há citações entre aspas simples, portanto, você não pode colocar uma aspa simples dentro de uma string entre aspas simples. Há, no entanto, um idioma que se parece com isso:

echo 'foo'\''bar'

Isso imprime foo'bar , porque o argumento para echo é feito da cadeia de três caracteres com aspas simples foo , concatenada com o único caractere ' (obtida ao proteger o caractere ' de seus caracteres significado especial através do \ precedente), concatenado com a cadeia de três caracteres com aspas simples bar . Então, embora isso não seja exatamente o que acontece nos bastidores, você pode pensar em '\'' como uma forma de incluir uma aspa simples dentro de uma string entre aspas simples.

Se você quiser imprimir strings multilinhas complexas, uma ferramenta melhor é o documento aqui . Um documento aqui consiste nos dois caracteres << seguidos por um marcador como <<EOF , depois algumas linhas de texto e, em seguida, o marcador final sozinho em sua linha. Se o marcador for citado de alguma forma ( 'EOF' ou "EOF" ou \EOF ou 'E "" OF' ou ...), então o texto é interpretado literalmente (como dentro de aspas simples, exceto que mesmo ' é um personagem comum). Se o marcador não for citado, o texto será interpretado como uma string com aspas duplas, com $\' retendo seu status especial (mas " e novas linhas são interpretadas literalmente).

cat <<'EOF'
echo PARAM='  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' '
EOF
    
por 16.11.2011 / 00:14
2

Ok, isso funcionará: -

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

Teste aqui: -

[asmith@yagi ~]$ echo ' echo PARAM='  grep  $ARG  /var/tmp/setfile  | awk '\''{print $2}'\'' '    '
 echo PARAM='  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' ' 
    
por 15.11.2011 / 17:41
0

A sugestão de Gilles de usar um documento aqui é muito legal, e até funciona em linguagens de script como o Perl. Como um exemplo específico baseado em sua resposta à questão do OP,

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;

Concedido, este exemplo é um pouco artificial, mas descobri que é uma técnica útil para, digamos, enviar instruções SQL para psql (em vez de cat ).

(Observe que qualquer caractere não-alfanumérico não-espacial pode ser usado no lugar do # na construção genérica de cotação acima, mas o hash parece se destacar bem para este exemplo e não é tratado como um comentário.)

    
por 10.11.2016 / 20:02
-1

Vamos dar o seu comando

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

e primeiro dividi-lo em palavras, usando espaço em branco sem aspas como delimitador:

Arg[1]=“' echo PARAM='  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“$2}' '    '”

Em seguida, fazemos a expansão de parâmetros: expanda $… , mas não dentro de '…' . Como $2 está vazio (a menos que você o defina por meio de, por exemplo, set -- … ), ele será substituído por uma string vazia.

Arg[1]=“' echo PARAM='  grep  $ARG  /var/tmp/setfile  | awk '{print”
Arg[2]=“}' '    '”

Em seguida, desmarque a remoção.

Arg[1]=“ echo PARAM='  grep  $ARG  /var/tmp/setfile  | awk {print”
Arg[2]=“} '    ”

Esses argumentos são então passados para echo, que os imprimirá um após o outro, separados por um único espaço.

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

Espero que esta instrução passo-a-passo torne o comportamento observado mais claro. Para evitar o problema, escute aspas simples assim:

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

Isso encerrará a string com aspas simples e, em seguida, adicionará uma aspas simples (com escape) à palavra e, em seguida, iniciará uma nova string entre aspas simples, tudo sem separar a palavra.

    
por 10.02.2014 / 16:04