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

23

Considere as duas amostras de shell

$ ls
myDoc.html
SomeDirectory
someDoc.txt

e

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

O primeiro executa ls , que, como eu entendo, acrescenta o conteúdo do diretório de trabalho atual ao arquivo stdout (que é o que o terminal exibe). Está correto?

O segundo usa o valor do comando ls (que significa o conteúdo do diretório de trabalho atual) e o imprime no arquivo stdout . Está correto?

Por que os dois comandos fornecem saídas diferentes?

    
por Owen 17.05.2016 / 09:44

2 respostas

67

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 de $(ls) e executa divisão de palavras . Com o padrão IFS , isso significa que todas as sequências de espaço em branco, incluindo os 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 a divisão de palavras, consulte Perguntas frequentes de Greg .

Suprimir a divisão de palavras

O shell não executa a divisão de palavras em strings entre aspas. Assim, você pode suprimir a divisão de palavras e manter a saída de múltiplas linhas com:

echo "$(ls)"

ls e saída de múltiplas linhas

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

$ ls
file1  file2  file3  file4  file5  file6

Este é o padrão quando a saída de ls vai para um terminal. Quando a saída não está indo diretamente para um terminal, ls altera seu padrão para um arquivo por linha:

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

Esse comportamento está documentado em man ls .

Outra sutileza: substituição de comandos e novas linhas finais

$(...) é substituição de comandos e o shell remove os caracteres de nova linha à direita da saída da substituição de comandos . Isso normalmente não é perceptível porque, por padrão, echo adiciona uma nova linha ao final de sua saída. Portanto, se você perder uma nova linha no final de $(...) e obtiver uma de echo , não haverá alteração. Se, no entanto, a saída do seu comando terminar com 2 ou mais caracteres de nova linha enquanto echo adicionar novamente apenas um, sua saída não terá uma ou mais novas linhas. Por exemplo, podemos usar printf para 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 em man bash .

Outra surpresa: expansão 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 file1 e file2 apareçam duas vezes na saída. Isso porque, como Jeffiekins aponta, a expansão do nome do caminho é executada primeiro pelo shell antes de o ls ser executado e novamente antes que o echo seja executado.

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

$ echo "$(ls file?)"
file?
file1
file2
    
por 17.05.2016 / 09:53
0

ls está ciente de que está enviando a saída para um terminal ou não.

A execução de ls em um prompt de comando gravará no seu pseudo-tty. Redirecionar a saída de ls geralmente não estará indo para uma pseudo-tty e ls formatará a saída de maneira diferente.

    
por 17.05.2016 / 22:17

Tags