Por que o eco está ignorando meus caracteres de citação?

24

O echocomando não está incluindo o texto completo que eu forneço. Por exemplo, se eu fizer:

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

Emite:

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 mudar para aspas duplas:

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

echonão produz nada! Por que está deixando de fora as aspas simples no primeiro exemplo e não produzindo nada no segundo? Como faço para que ele produza exatamente o que digitei?

Michael Mrozek
fonte
3
Não tenho certeza do que você está tentando alcançar aqui. Por que você está aninhando uma declaração de eco em outra? Ou você está literalmente tentando imprimir um comando (por exemplo ou mais)?
21711 Andy
Eu preciso inserir a saída echo para outro arquivo
Você precisa explicar o que está tentando fazer. Como "Quero anexar o seguinte texto: '...' em um arquivo."
MikeyB
2
Eu tenho tentado editar isso por 5 minutos, mas eu realmente não sei qual é a saída desejada. Edit : Ok, acho que tenho um palpite agora, então tentei editar. Se não for o que você pretendia me avise
Michael Mrozek
1
@ Michael - Uau - Se eu pudesse lhe dar algum representante para a sua edição, eu faria - bom trabalho fazendo desta uma ótima pergunta!
Randall

Respostas:

33

Seu shell está interpretando as aspas, ambas 'e "antes mesmo que elas cheguem echo. Geralmente, coloco aspas duplas em volta do meu argumento para ecoar, mesmo que desnecessárias; por exemplo:

$ echo "Hello world"
Hello world

Portanto, no seu primeiro exemplo, se você deseja incluir aspas literais em sua saída, elas precisam ser escapadas:

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

Ou eles já devem ser usados ​​dentro de um argumento citado (mas não pode ser o mesmo tipo de citação, ou você precisará escapar de qualquer maneira):

$ 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 - as tratam como variáveis ​​e as substituem por seus valores. Como provavelmente nenhuma dessas variáveis ​​está definida no seu shell, na verdade, apenas executa

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

Como grepapenas vê um argumento, ele assume que o argumento é o padrão que você está procurando e que o local em que ele deve ler os dados é stdin, impedindo a entrada de informações. É por isso que seu segundo comando parece travar.

Isso não acontecerá se você citar o argumento (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 citá-lo duas vezes, mas precisará escapar de $s para que o shell não os 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 \$2}' \`    '"
Michael Mrozek
fonte
7

Não vou entrar em muitos detalhes sobre por que suas tentativas se comportam dessa maneira, porque a resposta de Michael Mrozek cobre isso muito bem. Em poucas palavras, tudo entre aspas simples ( '…') é interpretado literalmente (e, em particular, a primeira 'marca o final da cadeia literal), enquanto mantém `e $seu significado especial entre "…".

Não há aspas dentro de aspas simples, portanto, você não pode colocar uma aspas única em uma sequência de aspas simples. Existe, no entanto, um idioma que se parece com ele:

echo 'foo'\''bar'

Isso é impresso foo'bar, porque o argumento to echoé feito da cadeia de três caracteres entre aspas simples foo, concatenada com o caractere único '(obtido ao proteger o caractere 'de seu significado especial através do anterior \), concatenada com a cadeia de três caracteres entre aspas simples bar. Portanto, embora não seja exatamente o que acontece nos bastidores, você pode pensar '\''em uma maneira de incluir uma única citação dentro de uma sequência de aspas simples.

Se você deseja imprimir seqüências complexas de várias linhas, uma ferramenta melhor é o documento aqui . Um documento aqui consiste nos dois caracteres <<seguidos por um marcador como <<EOF, em seguida, algumas linhas de texto, depois o marcador final sozinho em sua linha. Se o marcador for citado de alguma forma ( 'EOF'ou "EOF"ou \EOF'E "" OF "ou ...), o texto será interpretado literalmente (como dentro de aspas simples, exceto que mesmo 'é um caractere comum). Se o marcador não estiver entre aspas, o texto será interpretado como em uma sequência de aspas duplas, $\`mantendo seu status especial (mas as "novas linhas serão interpretadas literalmente).

cat <<'EOF'
echo PARAM=`  grep  $ARG  /var/tmp/setfile  | awk '{print $2}' `
EOF
Gilles 'SO- parar de ser mau'
fonte
1

Ok, isso vai funcionar: -

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

Testando aqui: -

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

A sugestão de Gilles de usar um documento aqui é muito boa e até funciona em linguagens de script como Perl. Como exemplo específico, com base 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;

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

(Observe que qualquer caractere não espacial não alfanumérico pode ser usado no lugar da #construção de aspas genérica acima, mas o hash parecia se destacar bem neste exemplo e não é tratado como um comentário.)

Randall
fonte
-1

Vamos assumir o seu comando

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

e primeiro divida-o em palavras, usando espaço em branco não citado como delimitador:

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

Em seguida, fazemos a expansão dos parâmetros: expanda, $…mas não dentro '…'. Como $2está vazio (a menos que você o defina 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, faça a remoção da cotação.

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

Esses argumentos são passados ​​para eco, 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, escape aspas simples como esta:

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

Isso encerrará a sequência de aspas simples, adicionará uma aspas simples (com escape) à palavra e iniciará uma nova sequência de aspas simples, tudo sem desmembrar a palavra.

MvG
fonte