Como o piping / redirection no Linux funciona exatamente?

1

Eu tenho esses três exemplos de redirecionamento stdin / stdout, apenas um deles está funcionando da maneira pretendida. Eu adoraria se alguém pudesse explicar isso para mim.

O objetivo é classificar o conteúdo no arquivo1 e salvar as alterações no mesmo arquivo.

  1. classificar arquivo1 | tee arquivo1 > / dev / null -------- > Funciona

  2. classificar arquivo1 | tee arquivo1 -------- > O conteúdo do arquivo1 será apagado

  3. classificar arquivo1 | tee arquivo1 > arquivo2 -------- > O conteúdo do arquivo1 será apagado

PS. tee copia a entrada padrão para cada FILE, e também para a saída padrão.

O que faz o primeiro exemplo funcionar?

    
por user3464156 06.05.2015 / 18:06

4 respostas

0

De acordo com meus testes no Debian Wheezy, todos os 3 cenários podem levar a ambos os resultados (arquivo1 é classificado e escrito de volta para si mesmo OU nada é classificado e nada é gravado no arquivo1.

Eu acredito que este é um comportamento normal e vem da maneira como o Linux trabalha com arquivos. Pense no comando - o comando sort começa a ler o arquivo1 e envia imediatamente sua saída para tee. Tee lê a saída, grava de volta no arquivo1 e imprime em / dev / null. Caso a ordenação seja rápida o suficiente para ler todo o arquivo1, o resultado final é ordenado. Mas no caso tee recebe o bloqueio no arquivo, ele apaga-lo (tee sempre apaga o arquivo de saída, exceto quando a opção append é usada) e isso é muito bonito o que está acontecendo em todos os seus 3 cenários.

Para torná-lo mais curto, digamos, às vezes, o tipo não é rápido o suficiente para ler o arquivo1. Nesse caso, tee apaga o arquivo ANTES que a classificação possa lê-lo.

Eu recomendaria o seguinte procedimento:

cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1

Caso você queira ver a saída classificada no stdout, faça assim:

cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1

Não é uma boa idéia deixar dois comandos diferentes trabalhando com 1 arquivo em sistemas multiprocessadores - você nunca pode ter certeza de qual deles será executado primeiro. Em um único sistema encadeado, o comportamento seria diferente - sequencial.

    
por 14.05.2015 / 06:22
2

Eu duvido que esse comportamento seja previsível (e certamente não dependesse disso). O comando tee provavelmente inicia um novo processo para enviar sua entrada para o destino 'outro'. O sistema operacional "bufferizará" a saída até chegar ao ponto em que cria o arquivo de destino e grava seu buffer temporário no arquivo. O momento exato em que isso acontece (e sobrescreve a fonte) provavelmente depende de:

  • O tamanho do arquivo e a memória disponível para o buffer
  • O tempo decorrido
  • Se a entrada do canal para tee terminar

Isso é mais profundo do que bash : é a maneira como os programas funcionam, que bash inicia. O shell apenas interpreta os comandos que você digita e inicia os programas necessários para executar os comandos. O shell não tem controle sobre como cada programa funciona e menos ainda sobre como esses programas interagem. Pedir a um programa (ou a um conjunto de programas) para extrair dados de um arquivo de entrada e gravar o resultado sobre o mesmo arquivo de entrada na mesma sentença é de responsabilidade do usuário.

Não se esqueça que o bash é apenas o interpretador dos comandos do usuário: é apenas um shell em torno do sistema operacional para converter as intenções do usuário em chamadas do sistema.

E documentado também! Ou este e-mail , que aborda problemas semelhantes. Ou este thread do StackOverflow . Ou este thread Serverfault .

Observe que isso também pode acontecer com o redirecionamento de stdin : se você receber comandos de entrada de um arquivo: $ myprog < commandfile . Se myprog gravar no arquivo de comando, não há garantia de que todos os comandos commandfile serão executados.

Uma analogia realmente básica seria algo como esta lista de instruções:

- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
  find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
  | grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'

Eu imagino que você faria uma cópia primeiro? (comando retirado do Guia Avançado de Roteiro de Bash )

    
por 06.05.2015 / 20:16
0

Então você quer que o conteúdo original do arquivo permaneça, enquanto anexando o arquivo com as alterações?

tee over escreve por padrão, tente usar o sinalizador -a para anexar o arquivo com as alterações.

    
por 06.05.2015 / 18:47
0
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1

Você pode gravar o arquivo em um espaço reservado, renomear o original para um backup e, em seguida, mover o espaço reservado para o original.

    
por 06.05.2015 / 18:54