Diferença entre 'ls' e 'echo $ (ls)'

27

Considere as duas amostras de casca

$ ls
myDoc.html
SomeDirectory
someDoc.txt

e

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

A primeira é executada lse, como eu entendo, anexa o conteúdo do diretório de trabalho atual ao stdoutarquivo (que é o que o terminal exibe). Isso está correto?

O segundo pega o valor do lscomando (que significa o conteúdo do diretório de trabalho atual) e o imprime no stdoutarquivo. Isso está correto?

Por que os dois comandos fornecem saídas diferentes?

Owen
fonte
11
porque você está fazendo eco. A saída de ls como entrada para o comando echo.
Ankidaemon 17/05
Esse é o comportamento "esperado", que alguém explicará em breve. No entanto, você tem certeza de que, lspor si só, não fornece vários itens por linha?
roaima 17/05
@roaima ls em colunas é um remanescente de um "recurso" bsd. as versões unix imprimem uma entrada por linha #
Steve Cox
@SteveCox Eu não uso o UNIX adequado desde o início do SVR4, então minha memória está um pouco nebulosa. Obrigado pela lembrança.
roaima 17/05
Por que não echo *?
Jdh8

Respostas:

70

Quando você executa este comando:

ls

o terminal exibe a saída de ls.

Quando você executa este comando:

echo $(ls)

o shell captura a saída $(ls)e executa a divisão de palavras nele. Com o padrão IFS, isso significa que todas as seqüências de espaço em branco, incluindo caracteres de nova linha, são substituídas por um único espaço em branco. É por isso que a saída de echo $(ls)aparece em uma linha.

Para uma discussão avançada sobre divisão de palavras, consulte as Perguntas frequentes de Greg .

Suprimindo a divisão de palavras

O shell não executa a divisão de palavras em cadeias entre aspas. Assim, você pode suprimir a divisão de palavras e reter a saída multilinha com:

echo "$(ls)"

ls e saída multilinha

Você deve ter notado que, lsàs vezes, imprime mais de um arquivo por linha:

$ ls
file1  file2  file3  file4  file5  file6

Esse é o padrão quando a saída de lsvai para um terminal. Quando a saída não vai diretamente para um terminal, lsaltera seu padrão para um arquivo por linha:

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

Esse comportamento está documentado man ls.

Outra sutileza: substituição de comando e novas linhas à direita

$(...)é substituição de comando e o shell remove os caracteres de nova linha à direita da saída da substituição de comando . Isso normalmente não é perceptível porque, por padrão, echoadiciona uma nova linha ao final de sua saída. Portanto, se você perder uma nova linha no final $(...)e a ganhar echo, não haverá alterações. Se, no entanto, a saída do seu comando terminar com 2 ou mais caracteres de nova linha enquanto echoadicionar apenas um, sua saída estará faltando uma ou mais novas linhas. Como exemplo, podemos usar printfpara gerar caracteres de nova linha à direita. Observe que os dois comandos a seguir, apesar do número diferente de novas linhas, produzem a mesma saída de uma linha em branco:

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

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

$ 

Esse comportamento está documentado man bash.

Outra surpresa: expansão do nome do caminho, duas vezes

Vamos criar três arquivos:

$ touch 'file?' file1 file2

Observe a diferença entre ls file?e echo $(ls file?):

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

No caso de echo $(ls file?), o arquivo glob file?é expandido duas vezes , fazendo com que os nomes dos arquivos file1e file2apareçam duas vezes na saída. Isso ocorre porque, como Jeffiekins aponta, a expansão do nome do caminho é realizada primeiro pelo shell antes de lsser executada e depois novamente antes da echoexecução.

A segunda expansão do nome do caminho pode ser suprimida se usamos aspas duplas:

$ echo "$(ls file?)"
file?
file1
file2
John1024
fonte
4
Além disso, usando isattyvocê pode verificar se stdout é um tty, ls usa isso para determinar se deve usar cores ou não.
Janus Troelsen
11
As aspas não são incompatíveis no último snippet de código? Ou $( )realmente fornece uma barreira de sintaxe para que você não precise interpolar?
Cat
6
O @cat $()fornece uma barreira de sintaxe.
#Registrado em
2
Mais uma diferença importante: se algum nome de arquivo contiver um caractere curinga, o echocomando expandirá o curinga . Por exemplo, se lsretornar arquivo? arquivo1 arquivo2 , então echo $(ls)retornará o arquivo? arquivo1 arquivo2 arquivo1 arquivo2 .
21416 Jeffiekins
11
@Jeffiekins echonão expande curingas; o shell faz antes de passar a expansão resultante como argumentos para echo.
Chepner # 18/16
0

ls sabe se está enviando saída para um terminal ou não.

A execução lsem um prompt de comando gravará em seu pseudo-tty. Redirecionar a saída de lsgeralmente não vai para um pseudo-tty e lsformatará a saída de maneira diferente.

mpez0
fonte