Por que o arquivo de arquivo shuf de comando deixa um arquivo vazio, mas comandos semelhantes não?

8

Eu sei que isso é uma espécie de duplicata de outra pergunta ( Por que este comando de classificação me dá um arquivo vazio? ), mas eu queria expandir a questão em resposta às respostas dadas.

O comando

shuf example.txt > example.txt

Retorna um arquivo em branco, porque o shell trunca o arquivo antes de embaralhá-lo, deixando apenas um arquivo em branco para ser arrastado. No entanto,

cat example.txt | shuf > example.txt

produzirá um arquivo embaralhado conforme o esperado.

Por que o método de pipeline funciona quando o redirecionamento simples não funciona? Se o arquivo for truncado antes de os comandos serem executados, o segundo método também não deve deixar um arquivo vazio?

    
por toryan 23.01.2014 / 00:58

6 respostas

20

O problema é que > example.txt começa a gravar nesse arquivo, antes de shuf example.txt começar a lê-lo. Portanto, como ainda não houve saída, example.txt está vazio, shuf lê um arquivo vazio e como shuf não produz saída nesse caso, o resultado final fica vazio.

Seu outro comando pode sofrer o mesmo problema. > example.txt pode matar o arquivo antes que cat example.txt comece a lê-lo; isso depende da ordem em que o shell executa essas coisas e quanto tempo leva cat para realmente abrir o arquivo.

Para evitar totalmente esses problemas, você pode usar shuf example.txt > example.txt.shuf && mv example.txt.shuf example.txt .

Ou você poderia usar shuf example.txt --output=example.txt .

    
por 23.01.2014 / 01:07
7

O pacote moreutils tem um comando sponge :

   sponge  reads  standard input and writes it out to the specified file.
   Unlike a shell redirect, sponge soaks up all its input before  opening
   the output file. This allows constricting pipelines that read from and
   write to the same file.

com o que você pode fazer:

shuf example.txt | sponge example.txt

( infelizmente o pacote moreutils também tem um util chamado parallel que é bem menos útil que o gnu paralelo. Eu removi o parallel instalado pelo moreutils )

    
por 23.01.2014 / 08:29
2

Você tem sorte de correr

cat example.txt | shuf > example.txt

não esvazia example.txt como este comando está fazendo.

shuf example.txt > example.txt

Redirecionamentos são realizados pelo shell antes dos comandos serem executados e os componentes do pipeline são executados simultaneamente.

Usar a opção -o / --output seria a melhor solução com shuf , mas se você gosta de correr riscos (muito leves), aqui está uma maneira não tradicional de evitar que o arquivo processado seja truncado antes de ser lido :

shuf example.txt | (sleep 1;rm example.txt;cat > example.txt)

e este mais simples e mais rápido, graças à sugestão de Ole:

(rm example.txt; shuf > example.txt) < example.txt
    
por 23.01.2014 / 08:20
1

No manual GNU bash (veja também, para o detalhes, seção 3.7 Executing Commands ):

3.1.1 Shell Operation

The following is a brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:

  1. Reads its input from a file (see Shell Scripts), from a string supplied as an argument to the -c invocation option (see Invoking Bash), or from the user’s terminal.
  2. Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by metacharacters. Alias expansion is performed by this step (see Aliases).
  3. Parses the tokens into simple and compound commands (see Shell Commands).
  4. Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments
  5. Performs any necessary redirections (see Redirections) and removes the redirection operators and their operands from the argument list.
  6. Executes the command (see Executing Commands).
  7. Optionally waits for the command to complete and collects its exit status (see Exit Status).

Considere a situação em que seu arquivo não existe. No entanto, seu segundo exemplo criará o arquivo na maioria das vezes . Se o arquivo não tivesse sido criado e não existisse e o redirecionamento não tivesse ocorrido, cat iria reclamar que não existe tal arquivo ... Na maioria das vezes, não. Para reproduzir o shuffle que você obteve com o segundo comando, eu precisava de um ótimo muitas tentativas . Então, de fato, a segunda expressão deve deixar um arquivo vazio na maioria das vezes.

    
por 23.01.2014 / 07:36
0

Você pode usar o Vim no modo Ex:

ex -sc '%!shuf' -cx example.txt
  1. % seleciona todas as linhas

  2. ! comando de execução

  3. x salvar e fechar

por 11.04.2016 / 05:28
0

A versão mais curta que encontrei:

(rm foo && shuf > foo) < foo

Isso abre o arquivo, desvincula-o e, em seguida, trunca o arquivo. Assim, você evita o arquivo truncado antes de abri-lo, que é o que você normalmente verá ao redirecionar a saída para o mesmo arquivo que você está lendo.

    
por 13.08.2017 / 10:31