Como funciona exatamente a tubulação/redirecionamento no Linux?

Como funciona exatamente a tubulação/redirecionamento no Linux?

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

O objetivo é classificar o conteúdo do 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 ARQUIVO e também para a saída padrão.

O que faz o primeiro exemplo funcionar?

Responder1

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

Acredito que esse seja um comportamento normal e vem da forma 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-a de volta no arquivo1 e imprime-a em/dev/null. Caso sort seja rápido o suficiente para ler todo o arquivo1, tee obtém a saída classificada. Mas caso tee obtenha o bloqueio do arquivo, ele o apaga (tee sempre apaga o arquivo de saída, exceto quando a opção anexar é usada) e é basicamente isso que está acontecendo em todos os seus três cenários.

Para resumir, digamos que às vezes a classificação não é rápida o suficiente para ler o arquivo1. Nesse caso, tee apaga o arquivo ANTES de sort poder 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 ideia deixar 2 comandos diferentes trabalhando com 1 arquivo em sistemas multiprocessadores - você nunca pode ter certeza de qual deles será executado primeiro. Em um sistema com thread único, o comportamento seria diferente – sequencial.

Responder2

Duvido que esse comportamento seja previsível (e certamente não dependeria disso). O teecomando provavelmente inicia um novo processo para enviar sua entrada para o 'outro' destino. O sistema operacional irá 'armazenar em buffer' a saída até atingir o ponto em que cria o arquivo de destino e grava seu buffer temporário no arquivo. O momento exato em que isso acontece (e substitui 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 tubo para teeterminar

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

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

E os seusdocumentado, também! Oueste e-mail, que aborda problemas semelhantes. Ou istoTópico StackOverflow. Oueste tópico 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 myproggravar no arquivo de comando, não há garantia de que todos commandfileos comandos 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 deGuia avançado de script Bash)

Responder3

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

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

Responder4

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 depois mover o espaço reservado para o original.

informação relacionada