Quando o shell encontra o texto entre $(
)
, ele:
- Leva para ser um comando e (como bodhi.zazen diz ) executa o comando em um subshell .
- Substitui a saída do comando, no lugar da expressão inteira (incluindo a abertura
$(
e fechamento )
).
Como a resposta do heartsmagic explica, isso é uma das duas sintaxes disponíveis para substituição de comando .
"O que ... faz com que a saída desses dois comandos seja a mesma?"
É realmente incomum que a saída de ls
seja exatamente igual à saída de echo $(ls)
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo $(ls)
bar foo
ls
normalmente separa nomes de arquivos por dois ou mais espaços, ou uma nova linha. Isso nos ajuda a distingui-los mais facilmente, especialmente porque espaços em nomes de arquivos são um pouco comuns (mas vários espaços consecutivos, menos comuns).
Divisão de palavras
Quando eu corri echo $(ls)
, aconteceu o seguinte.
-
Substituição de comandos substituiu $(ls)
por:
bar
foo
Você pode se surpreender ao ouvir isso, já que provavelmente não é o que você vê quando você executa ls
por si só! Essa disparidade em que ls
saídas é explicada abaixo, mas na verdade não é o motivo pelo qual você acaba com um único espaço entre essas duas palavras. O mesmo aconteceria se $(ls)
fosse substituído por bar foo
(com dois espaços).
-
Posteriormente, o shell executou divisão de palavras , tratando bar
e foo
como argumentos separados para o comando echo
em vez de como um único argumento contendo espaços.
Quando echo
recebe vários argumentos (exceto para opções como -n
, que são tratados especialmente e não impressos de todo), imprime todos eles, com um único espaço entre argumentos subseqüentes. echo
, em seguida, imprime uma nova linha (a menos que a opção -n
tenha passado).
Assim, echo $(ls)
mostra a saída como bar foo
em vez de bar foo
.
Se você executar ls
e não listar nenhum arquivo, ou apenas um arquivo, a saída de ls
será frequentemente igual à saída de echo $(ls)
. Mesmo assim, nem sempre será o mesmo, como quando um nome de arquivo contém espaço em branco diferente de espaços isolados (simples):
ek@Io:~/tmp2$ touch 'my file'
ek@Io:~/tmp2$ ls
my file
ek@Io:~/tmp2$ echo $(ls)
my file
Quando ls
imprime vários arquivos, sua saída provavelmente não será exatamente igual à saída de $(ls)
. Da mesma forma:
-
echo a b
e echo $(echo a b)
produzem a mesma saída, mas
-
echo 'a b'
e echo $(echo 'a b')
não.
Isso é importante para saber sobre a substituição de comandos - as substituições de comandos sem aspas estão sujeitas à divisão de palavras .
Detecção de terminal
Se você quiser evitar a divisão de palavras, você pode colocar a expressão para substituição de comandos entre aspas duplas ( "
). Isso geralmente é suficiente e, em particular, funcionará com os exemplos echo
-based acima, bem como o exemplo de ls
em um diretório com uma única entrada contendo espaços:
ek@Io:~/tmp2$ echo "$(ls)"
my file
No entanto, conforme observado acima, você pode se surpreender ao descobrir que o que vê quando executa ls
geralmente não é o que é passado para echo
no lugar de "$(ls)"
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo "$(ls)"
bar
foo
Isso ocorre porque ls
verifica se a saída padrão é um terminal para decidir como formatar sua saída , quando a formatação da saída não for explicitamente especificada.
- Quando stdout é um terminal,
ls
é gerado em colunas classificadas verticalmente, como ls -C
.
- Quando a stdout não é um terminal,
ls
lista cada entrada em sua própria linha, como ls -1
.
Na substituição do comando, a saída padrão não é um terminal porque a saída do comando não está sendo enviada diretamente para um terminal para você ver - em vez disso, ele está sendo capturado pelo shell e usado como parte de outro comando. / p>
Para obter a formatação de várias colunas ao executar ls
por meio da substituição de comandos, passe o -C
flag:
ek@Io:~/tmp$ echo "$(ls -C)"
bar foo
( dir
às vezes é sugerido como uma alternativa para ls -C
e também funcionará para isso, embora dir
se comporta como ls -C -b
em vez de meramente ls -C
.
Aliasing de shell
Outra razão pela qual ls
às vezes pode se comportar de maneira diferente de echo "$(ls)"
é que ls
pode ser um alias do shell . Execute alias ls
para verificar; no Ubuntu você normalmente recebe alias ls='ls --color=auto'
, o que significa que quando ls
aparece como a primeira palavra de um comando executado interativamente (e em algumas, mas não todas, outras circunstâncias), ele é substituído por ls --color=auto
.
Por exemplo, quando executo ls
, ele lista diretórios coloridos em azul e executáveis em verde (e implementa muitas outras regras de coloração também). --color=auto
faz com que ls
imprima a saída colorida quando a saída padrão é um terminal e não o contrário.
Para obter uma saída colorida ao executar ls
via substituição de comando, passe a porcentagem co_de% ou --color
opção . Se quiser, você pode combinar isso com --color=always
:
echo $(ls -C --color)
Observe que, embora você possa tornar -C
um alias para ls
ou ls --color
em vez de ls --color=always
, e ls --color=auto
não se importa se a saída padrão é um terminal ... esse alias ainda não será causa ls --color=always
para produzir saída colorida quando chamada por substituição de comando. Isso ocorre porque os aliases de shell (em shells estilo Bourne gostam de bash ) são apenas expandidos quando o shell os vê como a primeira palavra de um comando (e eles não são herdados por subshells).