O que o $ (ls * .txt) faz?

5

Em um terminal unix, se eu fizer isso:

egrep "Stuff" $(ls *.txt)

O resultado é óbvio. Mas o que faz

$(ls *.txt) faz por conta própria? Eu vejo que posso replicar o efeito por

egrep "Stuff" *.txt

Então, o que é $(ls *.txt) ?

    
por Bana 10.08.2017 / 05:48

5 respostas

28

"o que é que $(ls *.txt) do"

Resposta curta

$(ls *.txt) reúne uma lista de nomes de arquivos e e depois os processa . Não não use isso.

Resposta mais longa

  • ls destina-se a resultados legíveis por humanos. Como um dos mantenedores de ls escreveu em resposta a por que ls não ofereceria uma opção --null :

      

    Se fôssemos fazer isso, então essa é a interface que usaríamos. Contudo    ls é realmente uma ferramenta para consumo direto de um ser humano e, nesse   caso o processamento posterior é menos útil. Para mais processamento, find (1)   é mais adequado. [ênfase adicionada]

    Em outras palavras, o uso de ls para qualquer coisa que não seja a exibição para seres humanos simplesmente não é suportado. Os ls maintainers, por exemplo, recentemente alteraram o formato de saída padrão para algo que, embora fosse mais humano, sem aviso . Portanto, siga os conselhos deles: se você for fazer mais processamento de nomes de arquivos, use find em vez de ls .

  • $(...) é a substituição de comando. Uma conseqüência do uso da substituição de comando é que os caracteres de nova linha são removidos. Se o último nome de arquivo na lista contiver novas linhas à direita, elas serão removidas.

  • Como $(...) não tem aspas duplas, o texto produzido estará sujeito a:

    • divisão de palavras

    • Expansão do nome do caminho

    O resultado de ambos é mais uma falha nos nomes dos arquivos.

  • sem mencionar os problemas com nomes de arquivos que começam com - devido à falta de -- (para ls e egrep , pois o egrep do Ubuntu é a implementação do GNU que aceita opções mesmo depois de não -option argumentos a menos que POSIXLY_CORRECT esteja no ambiente).

  • também, se algum desses arquivos txt fosse do tipo diretório , ls listaria seu conteúdo em vez de eles mesmos.

  • e se não houver arquivos txt , a saída de ls estará vazia (embora você veja um erro sobre um arquivo *.txt ausente) e como egrep não receberá nenhum argumento de arquivo , ele procurará por Stuff em sua entrada padrão (e aparentemente parará).

Exemplo

Vamos criar 4 arquivos contendo Stuff em nosso diretório:

$ echo Stuff | tee file1 file2 'a b c.txt' 'f* .txt'
Stuff
$ ls -Q
"a b c.txt"  "file1"  "file2"  "f* .txt"

Agora, vamos executar o comando egrep:

$ egrep "Stuff" $(ls *.txt)
grep: a: No such file or directory
grep: b: No such file or directory
grep: c.txt: No such file or directory
file1:Stuff
file2:Stuff
f* .txt:Stuff
grep: .txt: No such file or directory

Observe que recebemos 4 mensagens de erro sobre arquivos inexistentes. Isso se deve a divisão de palavras . O resultado também mostra correspondências com dois arquivos, file1 e file2 , que não deveriam ter sido pesquisados porque não terminam com .txt . Isso é devido à expansão do _pathname '.

O comando escrito corretamente produz duas correspondências bem-sucedidas e nenhum erro:

$ egrep -- "Stuff" *.txt
a b c.txt:Stuff
f* .txt:Stuff

Solução recomendada

Uso:

egrep -- "Stuff" *.txt

ou POSIXly:

grep -E -- "Stuff" *.txt

ou:

grep -E -e Stuff -- *.txt

Isso funcionará com qualquer nome de arquivo e não tem nenhuma das limitações da abordagem ls .

    
por John1024 10.08.2017 / 06:13
6

O $( ) executa o comando ls *.txt e retorna o STDOUT do comando.

O que esse uso específico é, é um erro de programação newbie em pelo menos três níveis:

  1. egrep 'Stuff' *.txt funciona, como você disse, exceto pelos arquivos com nomes como A File.txt
  2. Usar ls output para a entrada do programa é insensato. Consulte as razões
  3. $( ls *.txt) manipula incorretamente nomes de arquivos com espaços e outros caracteres engraçados. find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff' é uma maneira mais resistente a balas.

Um exemplo inventado de expansão / confusão glob globular, em resposta a @ 8bittree é:

$ /bin/ls -l -b
total 36
-rw------- 1 w3 walt 2 Aug 11 13:41 A\ \012\ File.txt
-rw------- 1 w3 walt 2 Aug 11 13:42 A\ \n\ File.txt
-rw------- 1 w3 walt 2 Aug 11 13:40 A\ File.txt

$ grep "STUFF" $(ls *.txt)
grep: A: No such file or directory
grep: 2: No such file or directory
grep: File.txt: No such file or directory
grep: A: No such file or directory
grep: File.txt: No such file or directory
grep: A: No such file or directory
grep: File.txt: No such file or directory

$ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff'

$ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep '1'
./A File.txt:1
    
por waltinator 10.08.2017 / 06:13
3

Uau, por um momento, pensei que você estava se perguntando sobre '$(ls *.txt)' (com $(..) mais backquotes)

Como a pergunta já foi respondida, vou apenas avisá-lo sobre uma coisa. Aqui está uma sequência de comandos para mostrar por que isso pode ser perigoso:

  • set -x
    Para ver o que é executado
  • touch 'rm -f *.txt'
    Crie um arquivo de texto chamado 'rm -f * .txt', eu uso aspas simples aqui para que o Bash não expanda o curinga e os espaços.
  • '$(ls *.txt)'
    Aqui está a parte complicada. O comando ls *.txt será executado e, em seguida, redirecionará o resultado para STDOUT. Este resultado, rm -f *.txt será executado agora por causa dos backquotes.
    Então, o rm irá remover todos os arquivos de texto, no nosso caso o arquivo rm -f *.txt

Espero que você entenda, - como uma demonstração, apenas execute os comandos anteriores em um diretório vazio, para não quebrar nada.

    
por Sayardiss 10.08.2017 / 10:08
2
$( ... )

É uma substituição de comando, atribui a saída de um ou mais comandos como uma entrada para outro comando. A sintaxe antiga é: ' ... ' .

A diferença é que seus comandos serão executados em shells diferentes:

echo $BASHPID $( echo $BASHPID )
5718 7138

Na verdade, ele invoca um subshell:

echo $BASH_SUBSHELL $( echo $BASH_SUBSHELL )
0 1
    
por Ravexina 10.08.2017 / 07:55
0

Esta questão parece ter pouca utilidade no mundo real, mas

A melhor maneira de obter um resultado útil é:

echo $(ls *.txt)

Ou apenas

ls *.txt

Isso listará todos os arquivos com uma extensão .txt. Para encontrar arquivos de texto que contenham uma string específica,

egrep -l "Stuff" *.txt

Para mais informações sobre ferramentas de pesquisa, faça um

man egrep
    
por jones0610 10.08.2017 / 06:20