Perché l'eco ignora i miei caratteri di citazione?


24

Il echocomando non include il testo completo che gli ho dato. Ad esempio, se lo faccio:

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

Emette:

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

Le virgolette singole ( ') che avevo nel mio comando echo non sono incluse. Se passo a virgolette doppie:

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

echonon emette nulla! Perché tralascia le singole virgolette nel primo esempio e non emette nulla nel secondo? Come posso ottenere l'output esattamente quello che ho digitato?


3
Non sono sicuro di cosa stai cercando di ottenere qui. Perché stai annidando un'istruzione eco in un'altra? O stai letteralmente cercando di stampare un comando (per un esempio o simile)?
Andy Smith,

Devo inserire l'output dell'eco in un altro file

Devi spiegare cosa stai cercando di fare. Ad esempio "Voglio aggiungere il seguente testo: '...' a un file."
MikeyB,

2
Ho provato a modificarlo per 5 minuti, ma sul serio non so quale sia l'output desiderato. Modifica : Ok, penso di avere un'ipotesi ora, quindi ho provato a modificare. Se non è quello che intendevi, fammi sapere
Michael Mrozek

1
@Michael - Wow - Se potessi darti un rappresentante per la tua modifica, farei un bel lavoro, rendendolo una domanda davvero fantastica!
Randall,

Risposte:


33

La tua shell sta interpretando le virgolette, entrambe 'e ", prima ancora che arrivino echo. In genere metto solo doppie virgolette intorno al mio argomento, anche se non sono necessarie; per esempio:

$ echo "Hello world"
Hello world

Quindi nel tuo primo esempio, se vuoi includere virgolette letterali nel tuo output, entrambi devono essere sfuggiti:

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

Oppure devono essere già utilizzati all'interno di un argomento citato (ma non può essere lo stesso tipo di citazione, o dovrai comunque scappare):

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

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

Nel tuo secondo esempio, stai eseguendo una sostituzione di comando nel mezzo della stringa:

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

Le cose che iniziano con $sono anche gestite appositamente dalla shell: le tratta come variabili e le sostituisce con i loro valori. Poiché molto probabilmente nessuna di queste variabili è impostata nella shell, in realtà viene eseguita

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

Dal momento grepche vede solo un argomento, presuppone che l'argomento sia il modello che stai cercando e che il posto in cui dovrebbe leggere i dati sia stdin, quindi blocca in attesa di input. Ecco perché il tuo secondo comando sembra bloccarsi.

Questo non accadrà se citi una volta l'argomento (motivo per cui il tuo primo esempio ha quasi funzionato), quindi questo è un modo per ottenere l'output che desideri:

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

Puoi anche fare una doppia virgoletta, ma dovrai sfuggire alle $s in modo che la shell non le risolva come variabili e le backtick in modo che la shell non esegua subito la sostituzione dei comandi:

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

7

Non entrerò nei dettagli sul perché i tuoi tentativi si comportano nel modo in cui si comportano, perché la risposta di Michael Mrozek copre bene questo. In poche parole, tutto tra virgolette singole ( '…') viene interpretato letteralmente (e in particolare il primo 'segna la fine della stringa letterale), mentre `e $mantengono il loro significato speciale tra "…".

Non ci sono citazioni tra virgolette singole, quindi non è possibile inserire una virgoletta all'interno di una stringa a virgoletta singola. C'è comunque un idioma che assomiglia a questo:

echo 'foo'\''bar'

Questo stampa foo'bar, perché l'argomento a echoè fatto della stringa di tre caratteri a virgoletta singola foo, concatenata con il carattere singolo '(ottenuto proteggendo il carattere 'dal suo significato speciale attraverso il precedente \), concatenata con la stringa di tre caratteri a virgoletta singola bar. Quindi, anche se non è esattamente ciò che accade dietro le quinte, puoi pensare '\''a un modo per includere una singola citazione all'interno di una stringa a virgoletta singola.

Se vuoi stampare stringhe multilinea complesse, uno strumento migliore è il documento qui . Un documento qui è costituito dai due caratteri <<seguiti da un marcatore come <<EOF, quindi alcune righe di testo, quindi solo il marcatore finale sulla sua riga. Se il marcatore è citato in alcun modo ( 'EOF'o "EOF"o \EOF'E "" OF' o ...), il testo viene interpretato letteralmente (come all'interno di virgolette singole, tranne che 'è persino un carattere ordinario). Se il marcatore non è affatto tra virgolette, allora il testo viene interpretato come in una stringa tra virgolette doppie, $\`mantenendo il loro status speciale (ma "e le nuove righe vengono interpretate letteralmente).

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

1

Va bene, questo funzionerà: -

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

Test qui: -

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

0

Il suggerimento di Gilles di usare un documento qui è davvero carino e funziona anche con linguaggi di scripting come Perl. Come esempio specifico basato sulla sua risposta al problema del 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;

Certo, questo esempio è un po 'inventato, ma l'ho trovato una tecnica utile, per esempio, per inviare istruzioni SQL a psql(anziché cat).

(Nota che qualsiasi carattere non spaziale non alfanumerico può essere usato al posto del #costrutto di quotazione generico sopra, ma l'hash sembra distinguersi bene per questo esempio e non viene trattato come un commento.)


-1

Prendiamo il tuo comando

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

e prima dividerlo in parole, usando uno spazio bianco non quotato come delimitatore:

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

Successivamente, eseguiamo l'espansione dei parametri: espandi $…ma non all'interno '…'. Poiché $2è vuoto (a meno che non sia impostato tramite es. set -- …), Verrà sostituito da una stringa vuota.

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

Quindi, rimuovi la citazione.

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

Questi argomenti vengono quindi passati all'eco, che li stamperà uno dopo l'altro, separati da un singolo spazio.

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

Spero che questa istruzione passo-passo renda più chiaro il comportamento osservato. Per evitare il problema, sfuggire alle virgolette singole come questa:

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

Questo terminerà la stringa tra virgolette singole, quindi aggiungerà una virgoletta singola (con escape) alla parola, quindi inizierà una nuova stringa con virgolette singole, il tutto senza spezzare la parola.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.