Ordem dos comandos executados e redirecionamento [duplicado]

3

Para um script bash que estou fazendo, preciso excluir todos os arquivos, exceto os 2 mais novos, em um diretório.

Eu decidi usar ls -tUr > somefile.txt e depois seguir os 2 mais novos, movê-los para outro lugar, rm * e movê-los de volta.

Por algum motivo, somefile.txt aparece (entre os demais arquivos) dentro de somefile.txt , o que estraga usando o comando tail .

Então, minha pergunta é: logicamente, ls retornaria a pasta / arquivos atual e seria redirecionado para somefile.txt , mas claramente isso não está acontecendo, pois somefile.txt deve existir antes de ls executar; porque é isso?

    
por demcooltricks 16.05.2017 / 15:34

2 respostas

8

Isso responde à pergunta "por que isso acontece?"

Sim, somefile.txt será realmente criado antes que ls seja executado.

$ utility >file

A primeira coisa que acontece é que o shell percebe o redirecionamento, cria file (ou trunca se já existir), então executa utility com seu fluxo de saída padrão entrando em file .

O utilitário geralmente não está ciente ou preocupado com onde sua saída padrão está indo (alguns, como ls , verificam se é um TTY ou não e mudam seu comportamento de acordo), então não importa seja escrevendo para um arquivo, canal, arquivo de dispositivo ou soquete comum. Certamente não está preocupado em criar este arquivo. Portanto, o trabalho do shell é garantir que o encanamento esteja em vigor antes que o processo seja iniciado, o que inclui a criação ou o truncamento do arquivo denominado file no exemplo acima.

Para as respostas que tratam do problema relacionado à exclusão de todos os arquivos, exceto os mais recentes, consulte a pergunta " remove os arquivos mais antigos "

    
por 16.05.2017 / 15:47
2

Embora Kusalananda tenha respondido à sua pergunta sobre o porquê somefile.txt aparece na saída de ls , tentarei abordar a questão sobre como excluir todos, exceto os dois arquivos criados mais recentes como a questão vinculada lida com os arquivos modificados mais recentes.

Primeiro, você não precisa gravar a saída em um arquivo. Os comandos podem interagir uns com os outros diretamente, é para isso que servem os canos.

Aqui, você poderia fazer:

ls -tU | tail -n +3 | xargs echo rm -f -- # echo for dry-run

Mas isso é falho no caso geral, pois tail trabalha em linhas e xargs trabalha em palavras (possivelmente citadas), e não é garantido que nenhum desses nomes seja um desses. Eles nem mesmo têm garantia de texto válido. Ele só funcionaria corretamente se nenhum dos nomes de arquivo contivesse espaços em branco, novas linhas, aspas, barras invertidas ou seqüências de bytes não formando caracteres válidos.

Se o sistema é FreeBSD (como seu uso de -U sugere), você poderia usar o formato de saída json que é confiável como:

ls --libxo=json -Ut |
  perl -MJSON::PP -l0 -0777 -ne '
    @files = map {$_->{name}} @{
      decode_json($_)->{"file-information"}->{directory}->[0]->{entry}
    };
    print for @files[2..$#files]' | xargs -0 echo rm -f --
    
por 16.05.2017 / 16:36