Perché shell Command Substitution assorbe un carattere newline finale?


25

Come nel seguente esempio, e come nella mia recente domanda In bash, dove è finito il carattere newline finale? , Voglio sapere "perché" succede

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

Presumo che ci debba essere una ragione molto significativa per un'azione shell, vale a dire la sostituzione dei comandi, per eliminare effettivamente alcuni dati dall'output del comando che sta sostituendo ...
ma non riesco a capire come funziona, come sembra l'antitesi di ciò che dovrebbe fare .. cioè. restituire l'output di un comando al processo di script ... Trattenere un personaggio mi sembra strano, ma suppongo che ci sia una ragione ragionevole per farlo ... Sono curioso di scoprire qual è quella ragione ... .


Risposte:


22

Perché la shell non era originariamente concepita per essere un linguaggio di programmazione completo.

È abbastanza difficile rimuovere un finale \ndall'output di un comando. Tuttavia, a scopi di visualizzazione, quasi tutti i comandi terminano il loro output con \n, quindi ... ci deve essere un modo semplice per rimuoverlo quando si desidera utilizzarlo in un altro comando. La rimozione scelta con la $()costruzione è stata la soluzione scelta.

Quindi, forse accetterai questa domanda come risposta:

Riesci a trovare un modo semplice per rimuovere il trailing \nse questo non è stato fatto automaticamente nel seguente comando?

> echo The current date is "$(date)", have a good day!

Si noti che è necessario un preventivo per evitare la distruzione di doppi spazi che possono apparire in date formattate.


1
Se quello che dici è vero, sarei assolutamente sbalordito. Se c'è un modo shoptper cambiare il comportamento predefinito descritto, sarei di nuovo felice ... Trovo difficile pensare che Bash / Linux / Unix inquinino una funzione così critica come "catturare lo stdout" solo perché datenon ha un'opzione con / senza '\ n' ... La soluzione ovvia sarebbe che bash (o altra shell) ha un shoptper questo ... Ne conosci qualcuno? .. e hai dei link di riferimento che parlano dei perché e dei motivi di questo problema? ... e perché non dateha un'opzione \ n .. Dovrebbe!
Peter

2
Le sostituzioni di comandi sono apparse nella prima versione della shell Bourne nella "7a edizione" di Unix nel 1979. Era già una grande rivoluzione e immagino (non sono nato) che a nessuno importasse tenere traccia di "\ n" o no. Sì, puoi leggere la manpage in meno di 10 minuti e sembra che nulla sia cambiato sulla sostituzione dei comandi fino ad allora. Tranne la $()sintassi alternativa preferita .
Stéphane Gimenez,

Grazie ... Ho pensato che questo potesse essere un problema legato al passato, e sicuramente si sta dando forma al fatto che è meglio che inizi a guardare le mie sostituzioni di comando con più attenzione. Fino ad oggi devo essere sopravvissuto su un'ala e una preghiera .. Alcuni di quegli "eventi misteriosi" che ho incontrato stanno iniziando a dare un senso ora :) ... Quindi in un caso contrario del tuo "appuntamento" positivo, se Voglio mantenere il mio trascinamento \ n, devo codificare qualcosa del genere :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. così sia :)
Peter.O

PS sono il mio commento sopra: certo, datefunzionerebbe, ma ho usato solo dateun esempio di qualsiasi comando ..
Peter.O

La tua "domanda" sulla data è stata la più forte promessa verso la mia comprensione del "perché" Command Substution fa quello che fa, quindi questa è stata la "migliore" risposta alla mia domanda ... e sicuramente avevo anche bisogno delle altre buone risposte ..
Peter

19

Fa parte dello standard :

La shell espande la sostituzione del comando eseguendo il comando in un ambiente subshell (consultare Shell Execution Environment ) e sostituendo la sostituzione del comando (il testo del comando più il "$ ()" o backquotes racchiuso) con l'output standard del comando, rimuovendo sequenze di una o più <newline> s alla fine della sostituzione.

Ovviamente, lo standard è probabilmente scritto in questo modo perché è così ksho qualcosa del genere, ma è lo standard ed è un comportamento documentato. Se non ti piace, usa Perl o qualcos'altro che ti consentirà di mantenere le nuove righe finali.


Un bel riassunto .. Grazie .. e i collegamenti hanno sicuramente aiutato
Peter.O

4
Lo standard è così perché è così che ha fatto la shell Bourne e da allora tutte le altre shell.
Gilles 'SO- smetti di essere malvagio' il

6

Beh, ha senso per me. La nuova riga è presente solo in primo luogo, nell'output del comando normale, in modo che il prompt appaia dopo il completamento del comando su una nuova riga. La nuova riga non fa parte dell'output originale nella maggior parte dei casi, è lì per riordinare lo schermo. Quando analizzi l'output di un comando, la nuova riga alla fine è di solito problematica. Perché il wccomando genera 2 righe di testo? Oh, non lo fa, ne esce uno seguito da una nuova riga. Quando analizzi wcnon vuoi preoccuparti del fatto che ci sono 2 linee di output - non ce ne sono davvero, ce n'è solo una.


@EightBitTony: la mia domanda non è in alcun modo correlata alla visualizzazione di qualcosa nel terminale .. È abbastanza semplice. Se è presente una nuova riga, verrà visualizzata la nuova riga. Il punto in questione qui è che a \nnel stdoutflusso originale viene rimosso da Sostituzione comandi. vale a dire. i miei dati originali (dati molto importanti) sono stati rimossi dalle nuove righe, anche quando i dati sono racchiusi tra "virgolette". Comprendo ragionevolmente come e perché funziona la suddivisione in parole, ma non ho assolutamente idea del perché la sostituzione dei comandi rimuova solo le nuove righe e solo quelle finali .
Peter

1
Nulla di ciò che hai detto cambia la mia opinione. Il più delle volte, la nuova riga finale in qualsiasi output è presente esclusivamente a scopo di visualizzazione, non come parte dei dati.
EightBitTony,

Sono d'accordo con la tua opinione e ho tutto il tempo ... Sì, la nuova riga alla fine dell'output è per comodità ... ma il mio problema non ha nulla a che fare con quello ... Sto chiedendo: Perché Command Substitution lo rimuove forzatamente ? ... Questi sono due diversi problemi. Forse la mia domanda dovrebbe essere: esiste una soluzione alternativa? . perché dover codificare questo problema aggiungendo un carattere fittizio finale, fa schifo! ... Lo farò se devo, ma questa è la prima volta che sono stato ostacolato da bash (e sono in stato di shock :) .. Fa così tanto bene ...
Peter.O

Come menzionato in un'altra risposta, fa parte dello standard. Per me, penso che lo faccia in questo modo perché quando postprocedi i dati vuoi solo vedere i dati, non la nuova riga conveniente. È perché la shell si aspetta che tu stia gestendo i dati in una pipe, e la newline non sono dati. Ancora e dobbiamo portarlo a una discussione.
EightBitTony,

Grazie .. L'idea mi è venuta abbastanza bene ora. Sembra che un caso di sostituzione dei comandi si limiti semplicemente a far cadere le nuove linee finali, piuttosto che a mantenerle .. Deve esserci un po 'di saggezza in quella mossa che non ho sentito " "ancora, ma pensavo che l'uso schiacciante e popolare di tutte le lettere minuscole nei nomi dei file fosse un po 'strano / inutile, ma pensavo proprio l'altro giorno che in realtà è un sistema abbastanza comodo .. Probabilmente vedere (più profondamente) la rima e la ragione del comportamento della Sostituzione Comando, un giorno ...
Peter

1

Stavo riscontrando lo stesso problema e ho trovato l'esempio seguente. Sembra che la citazione abbia aiutato la situazione, almeno per me:

http://tldp.org/LDP/abs/html/commandsub.html .

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

2
Ecco cosa sta succedendo qui. Supponiamo che tu abbia una variabile $str, contenente la stringa "a b c". Se si passa $stra echosenza virgolette ( echo $str), echoriceverà a, be ccome tre argomenti separati. Alla tua shell non interessa lo spazio bianco tra gli argomenti. echostamperà quindi quegli argomenti con spazi che li separano, in modo da ottenere "a b c". Se si passa la variabile echotra virgolette ( echo "$str"), la shell la vede come un argomento e la echoriceve come tale, quindi lo spazio bianco non viene compresso. Lo stesso vale per le newline.

1
Il problema che il poster originale sta avendo è che le nuove righe finali (solo le ultime) dei suoi comandi stanno scomparendo. A meno che non venga passato il -nflag, echoaggiunge una nuova riga finale al suo output, quindi entrambi gli echi nel tuo esempio hanno nuove righe finali. Tuttavia, se provi echo -n $dir_listingo echo -n "$dir_listing", vedrai che nessuna nuova riga finale viene stampata con o senza virgolette $dir_listing, il che suggerisce che il problema è con la sostituzione del comando.

2
tldp è un posto molto obsoleto per conoscere lo scripting della shell. Prova invece il Wooledge Bash Wiki. mywiki.wooledge.org/BashGuide
Wildcard il

-1

perché echo "hello "(senza virgolette) inghiotte lo spazio? il valore dei caratteri IFS è visto dalla shell come delimitatore, per cui quelli finali (che non delimitano nulla) vengono rimossi. la sostituzione di encomi è semplicemente eseguendo gli encomi in una sotto-shell, quindi segue la stessa logica.


@Philomath: Entrambi x="hello  "e x="hello     "perderanno tutti i loro spazi finali se vengono visualizzati tramite echo $x... Tuttavia, viene persa solo l'ultima riga del comando sostitutivo.
Peter

strano, non vedo alcuna differenza nella mia bash (verifica con xxd)
Philomath

Chiedo solo questo ... Rimuove tutte le nuove righe finali ... Stavo sperimentando IFS quando ho avuto quell'impressione, quindi cancelliamo quella :) .. ma la domanda rimane ancora ... È una parte fondamentale la suddivisione delle parole di condensare spazi multipli in un singolo spazio, e per rimuovere totalmente spazi iniziali e finali .. io posso capire che, ma certamente non capisco perché, con la sostituzione del comando, esso solo strisce \ n e non tutti gli spazi bianchi (come con la divisione delle parole), e perché solo la fine finale? .. e soprattutto: "Sostituzione comando" sostituisce solo parzialmente. Perché?
Peter

4
IFSnon ha nulla a che fare con lo stripping di nuove righe alla fine delle sostituzioni di comando.
Gilles 'SO- smetti di essere malvagio' il
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.