Come posso fare in modo che l'operatore backtick bash mantenga le nuove righe in output?


36

C'è un modo per fare in modo che bash non mangi newline nella sostituzione del backtick?

Per esempio:

var=`echo line one && echo line two`
echo $var

line one line two

Quello che voglio è

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

Risposte:


54

Non è un problema con la sostituzione dei backtick, ma con echo; devi citare la variabile per far funzionare i personaggi di controllo:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two

Quindi, bash rimuove le nuove righe mentre espande una variabile sul comando? E quel comportamento può essere disabilitato citando la variabile?
slowpoison

7
bashespansione effettua parametri prima di eseguire un comando, citando è necessario informare bashche si desidera stampare una singola stringa e non 4 parole ( line, one, linee two). Prova: echo a <many spaces> be echo "a <many spaces> b". Dai un'occhiata anche a questa risposta .
cYrus il

5

Anche se @cyrus è corretto, in realtà non risponde all'intera domanda e non vi è alcuna spiegazione di ciò che sta accadendo.

Quindi passiamo attraverso di esso.

Newline in stringa

Innanzitutto determinare la sequenza di byte prevista:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Ora, usa Sostituzione comandi (Sezione 3.5.4) per tentare di catturare questa sequenza di byte:

$ x=$( echo a; echo b; )

Quindi, fai una semplice eco per verificare la sequenza di byte:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Quindi sembra che la prima nuova riga sia stata sostituita con uno spazio e la seconda nuova riga sia stata lasciata intatta. Ma perché ?

Vediamo cosa sta realmente accadendo qui:

Innanzitutto, bash eseguirà l' espansione dei parametri della shell (sezione 3.5.3)

Il carattere '$' introduce l'espansione dei parametri, la sostituzione dei comandi o l'espansione aritmetica. Il nome del parametro o il simbolo da espandere può essere racchiuso tra parentesi graffe, che sono opzionali ma servono a proteggere la variabile da espandere dai caratteri immediatamente successivi che possono essere interpretati come parte del nome.

Quindi bash eseguirà la suddivisione in parole (Sezione 3.5.7)

La shell analizza i risultati dell'espansione dei parametri, della sostituzione dei comandi e dell'espansione aritmetica che non si sono verificati tra virgolette doppie per la suddivisione delle parole.

La shell tratta ogni carattere di $ IFS come un delimitatore e divide i risultati delle altre espansioni in parole su questi caratteri. Se IFS non è impostato o il suo valore è esattamente, ...

Successivamente, bash lo tratterà come un semplice comando (Sezione 3.2.1)

Un comando semplice è il tipo di comando che si incontra più spesso. È solo una sequenza di parole separate da spazi, terminate da uno degli operatori di controllo della shell (vedi Definizioni). La prima parola generalmente specifica un comando da eseguire, con il resto delle parole come argomenti di quel comando.

La definizione di spazi vuoti (Sezione 2 - Definizioni)

vuoto Uno spazio o un carattere di tabulazione.

Infine, bash invoca il comando interno echo (Sezione 4.2 - Comandi incorporati di Bash)

... Emette gli arg, separati da spazi, terminati con una nuova riga. ...

Quindi, per riassumere, le nuove righe vengono rimosse da Word Splitting, quindi echo ottiene 2 arg, "a" e "b", e quindi li emette separati da spazi e termina con una nuova riga.

Seguendo le indicazioni di @cyrus (e sopprimendo la nuova riga da echo con -n) il risultato è migliore:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Newline alla fine della stringa

Tuttavia, non è ancora perfetto, la nuova riga finale è scomparsa. Osservando più da vicino la sostituzione dei comandi (sezione 3.5.4) :

Bash esegue l'espansione eseguendo il comando e sostituendo la sostituzione del comando con l'output standard del comando, eliminando tutte le nuove righe finali.

Ora che è chiaro perché la newline scompare, bash può essere ingannato nel mantenerlo. Per fare ciò, aggiungi una stringa aggiuntiva alla fine e rimuovila quando usi la variabile:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Aggiungi parte extra alla fine dell'output e cita le variabili:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46

0

Avrei inserito la mia piccola aggiunta in un commento sulla risposta di cYrus, ma non posso ancora commentare le risposte che penso. Quindi duplicherò parte di cYrus la sua risposta per completezza.

Ciò che accade nella prima riga del codice è che in effetti la sostituzione dei backtick mangia le nuove righe finali dell'output del comando sostituito, come documentato . Ma non mangia la nuova riga tra le tue due linee.

Ciò che accade nella seconda riga di codice è che l'eco viene eseguito con due argomenti, la prima e la seconda riga. La quotazione della variabile espansa risolverà ciò: echo "$var"Ciò preserverà la nuova riga tra la riga uno e la riga due. E echoaggiungerà una nuova riga finale per impostazione predefinita in modo che tutto sia a posto.

Ora, se si desidera preservare tutte le nuove righe finali nell'output di una sostituzione di comando, una soluzione alternativa consiste nell'aggiungere un'altra riga all'output, che è possibile rimuovere dopo la sostituzione del comando, vedere qui .

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.