Quando eu redireciono a saída de ls para um arquivo, o nome do arquivo é incluído nesse arquivo. Como posso evitar isso?

4

Observe:

$ ls
$ ls > list
$ cat list
list

Isso parece indicar que quando ls é executado, o redirecionamento para o arquivo list já começou e o arquivo list já foi criado. Uma explicação suficientemente boa, de qualquer forma, mas a questão é a seguinte: como posso evitar que isso aconteça? O que eu esperava que acontecesse era ls seria executado e sua saída seria despejada em list e é isso que eu quero.

    
por Steven Lu 16.07.2016 / 01:50

5 respostas

12

Como você percebeu, o arquivo é criado antes que ls seja executado. Isso se deve ao modo como o shell lida com sua ordem de operações. Para fazer

ls > file

o shell precisa criar file e, em seguida, definir stdout para apontar para isso e finalmente executar o programa ls .

Então você tem algumas opções.

  1. Crie o arquivo em outro diretório (por exemplo, /tmp ) e, em seguida, mv para o diretório final
  2. Crie como um arquivo oculto ( .file ) e renomeie-o
  3. Use grep para remover o arquivo da saída
  4. Cheat: -)

A fraude seria algo como

x=$(ls) ; printf "%s\n" "$x" > file

Isso faz com que a saída de ls seja mantida em uma variável e, em seguida, escrevemos isso.

    
por 16.07.2016 / 02:08
9

O arquivo de saída é criado pelo shell antes de ls começar. Você pode contornar isso usando tee :

ls | tee list

Para derrotar completamente qualquer condição de corrida, sempre há

ls | grep -vx 'list' > list

Ou se você gostar, tee exibirá os resultados também:

ls | grep -vx 'list' | tee list

No entanto, como apontado nos comentários, coisas como essa geralmente quebram quando os nomes de arquivos contêm caracteres estranhos. Os nomes de arquivos Unix geralmente podem conter qualquer caractere, exceto NUL e / , portanto, analisar a saída de ls é extremamente difícil:

  • A atribuição a uma variável de shell pode falhar se um nome de arquivo terminar em um ou mais \n .
  • A filtragem com grep falha quando o termo de pesquisa está entre \n .
  • Você pode separar nomes de arquivos com NUL em vez de \n usando find , mas pode ser difícil convertê-los em algo parecido com a saída classificada tradicionalmente separada por nova linha de ls .
  • A remoção do nome do arquivo de saída da lista pode estar incorreta se já existir.

Assim, a única maneira realmente eficaz de fazer isso é criar o arquivo de saída em outro lugar e movê-lo para o lugar. Se você nunca usará ls -a , isso funciona:

ls > .list && mv .list list

Se você estiver usando ls -a , .list poderá aparecer em sua saída, mas não existirá mais no diretório. Então você usaria um diretório diferente, como /tmp para armazenar o resultado intermediário. Claro, se você sempre usa /tmp , você tem problemas, então você pode escrever um script:

#!/bin/sh
OUTDIR='/tmp'
if [ "${PWD}" = '/tmp' ]; then
  OUTDIR="${HOME}"
fi
ls > "${OUTDIR}/list" && mv "${OUTDIR}/list" list

Isso parece muito complicado para a tarefa, no entanto.

Mas a causa inteira do problema é que o shell está criando o arquivo de saída antes do início do comando. Podemos levar isso em consideração e apenas fazer com que a shell liste os arquivos para nós. Então nós nem sequer precisamos de ls !

printf '%s\n' * > list

Isso funcionará até que você tenha muitos arquivos no diretório para caber em uma lista de argumentos.

    
por 16.07.2016 / 01:52
3

Você pode tornar o nome do arquivo temporariamente oculto:

ls >.list && mv .list list
    
por 16.07.2016 / 01:51
3

Você pode usar moreutils sponge :

ls | sponge list

Ou com zsh :

cp =(ls) list

Com o GNU ls :

ls -I list > list

(embora se houvesse um arquivo chamado list antes, isso significa que ele não será listado).

Como ls output é classificado de qualquer forma, você também pode usar (supondo que seus nomes de arquivos não contenham caracteres de nova linha):

ls | sort -o list

Ou para evitar a classificação dupla, se o seu ls suportar -U :

ls -U | sort -o list
    
por 16.07.2016 / 12:32
2

Parcial / mais crédito vai para @SphenHarris ...

echo "'ls'" > list

equivalente a

echo "$(ls)" > list
    
por 16.07.2016 / 02:19