Differenza tra 'ls' e 'echo $ (ls)'


27

Considera i due campioni di shell

$ ls
myDoc.html
SomeDirectory
someDoc.txt

e

$ echo $(ls)
myDoc.html SomeDirectory someDoc.txt

lsViene eseguito il primo che, a quanto ho capito, aggiunge il contenuto della directory di lavoro corrente al stdoutfile (che è ciò che viene visualizzato dal terminale). È corretto?

Il secondo prende il valore del lscomando (il che significa che è il contenuto della directory di lavoro corrente) e lo stampa sul stdoutfile. È corretto?

Perché i due comandi forniscono output diversi?


1
perché stai facendo eco. L'output di ls come input per il comando echo.
ankidaemon,

Questo è un comportamento "previsto", che qualcuno spiegherà a breve. Tuttavia, sei sicuro che lsda solo non ti dà più elementi per riga?
roaima,

@roaima columnized ls è un residuo di una "caratteristica" bsd. le versioni unix stampano una voce per riga
Steve Cox,

@SteveCox Non ho usato UNIX corretto sin dai primi SVR4, quindi la mia memoria è un po 'confusa. Grazie per il promemoria.
roaima,

Perché no echo *?
jdh8,

Risposte:


70

Quando si esegue questo comando:

ls

il terminale visualizza l'uscita di ls.

Quando si esegue questo comando:

echo $(ls)

la shell cattura l'output di $(ls)ed esegue la divisione delle parole su di esso. Con l'impostazione predefinita IFS, ciò significa che tutte le sequenze di spazi bianchi, inclusi i caratteri di nuova riga, vengono sostituite da un singolo spazio vuoto. Ecco perché l'output di echo $(ls)appare su una riga.

Per una discussione avanzata sulla suddivisione delle parole, vedere le FAQ di Greg .

Sopprimendo la divisione delle parole

La shell non esegue la divisione delle parole tra stringhe tra virgolette. Pertanto, è possibile eliminare la suddivisione delle parole e conservare l'output multilinea con:

echo "$(ls)"

ls e output multilinea

Potresti aver notato che a lsvolte stampa più di un file per riga:

$ ls
file1  file2  file3  file4  file5  file6

Questo è il valore predefinito quando l'output di lsva a un terminale. Quando l'output non sta andando direttamente a un terminale, lscambia il suo valore predefinito in un file per riga:

$ echo "$(ls)"
file1
file2
file3
file4
file5
file6

Questo comportamento è documentato in man ls.

Un'altra sottigliezza: sostituzione dei comandi e nuove righe finali

$(...)è la sostituzione dei comandi e la shell rimuove i caratteri di nuova riga finali dall'output della sostituzione dei comandi . Questo normalmente non è evidente perché, per impostazione predefinita, echoaggiunge una nuova riga alla fine del suo output. Quindi, se perdi una nuova linea dalla fine di $(...)e ne guadagni una echo, non ci sono cambiamenti. Se, tuttavia, l'output del comando termina con 2 o più caratteri di nuova riga mentre ne echoaggiunge solo uno, nell'output mancheranno una o più nuove righe. Ad esempio, possiamo usare printfper generare caratteri di nuova riga finali. Si noti che entrambi i seguenti comandi, nonostante il diverso numero di nuove righe, producono lo stesso output di una riga vuota:

$ echo "$(printf "\n")"

$ echo "$(printf "\n\n\n\n\n")"

$ 

Questo comportamento è documentato in man bash.

Un'altra sorpresa: l'espansione del nome percorso, due volte

Creiamo tre file:

$ touch 'file?' file1 file2

Osserva la differenza tra ls file?e echo $(ls file?):

$ ls file?
file?  file1  file2
$ echo $(ls file?)
file? file1 file2 file1 file2

Nel caso di echo $(ls file?), il file glob file?viene espanso due volte , causando i nomi dei file file1e file2apparire due volte nell'output. Questo perché, come sottolinea Jeffiekins, l' espansione del nome percorso viene eseguita prima dalla shell prima che lsvenga eseguita e poi ancora primaecho venga eseguita.

La seconda espansione del nome percorso può essere soppressa se abbiamo usato le virgolette doppie:

$ echo "$(ls file?)"
file?
file1
file2

4
inoltre, usando isattypuoi verificare se stdout è un tty, usa questo per determinare se usare i colori o meno.
Janus Troelsen,

1
Le virgolette non sono abbinate nell'ultimo snippet di codice? O $( )effettivamente fornire una barriera di sintassi in modo da non dover interpolare?
gatto,

6
@cat $()fornisce una barriera di sintassi.
Casuale 832,

2
Un'altra differenza importante: se un nome file contiene un carattere jolly, il echocomando espanderà il carattere jolly . Ad esempio, se lsrestituisce il file? file1 file2 , quindi echo $(ls)restituirà il file? file1 file2 file1 file2 .
Jeffiekins,

1
@Jeffiekins echonon espande i caratteri jolly; la shell fa prima di passare l'espansione risultante come argomento a echo.
Chepner,

0

ls è consapevole se sta inviando output a un terminale o meno.

L'esecuzione lsal prompt dei comandi scriverà sulla tua pseudo-tty. Il reindirizzamento dell'output di lsgeneralmente non passerà a una pseudo-tty e lsformatterà l'output in modo diverso.

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.