Redirecione a saída de todos os comandos para o arquivo, não apenas da última

11

Quero adicionar um cabeçalho a um arquivo com eco e, em seguida, usar um comando para criar o restante do arquivo. Isso significa que vou usar dois comandos separados.

Como gravar a saída de ambos os comandos em um arquivo com redirecionamento?

eu tentei

echo "header line" | cut -c 1-5 input_file > output_file

echo "header line"; cut -c 1-5 input_file > output_file

Isso redireciona apenas a saída do comando recortar.

O comando a seguir funciona, mas parece desajeitado:

echo "header line" > output_file; cut -c 1-5 input_file >> output_file

Qual é a maneira inteligente de resolver meu problema?

The Unfun Cat
fonte

Respostas:

11

O problema aqui não é um problema com o redirecionamento do Linux; pelo contrário, é um mal-entendido fundamental de como o pipeline funciona. O redirecionamento aqui não está funcionando porque apenas o corte é realmente impresso em stdout. O stdout para o comando echo foi direcionado para o stdin do cut (que não está sendo usado neste caso desde que um arquivo foi especificado).

echo "header line" > output_file && cut -c 1-5 input_file >> output_file

é o que você deseja e não é deselegante (substituí-o ;por, &&para que o comando cut seja executado apenas se o cabeçalho for gravado com sucesso; dessa forma, não será executado se você não tiver permissões para criar ou gravar em output_file )

Você também pode fazer tudo em uma subcama, por exemplo.

(echo "header line"; cut -c 1-5 input_file) > output_file

mas não há benefício real em fazer isso e, com exemplos mais complexos, pode causar problemas se você não estiver familiarizado com o escopo do subshell.

Se você deseja cortar para passar de stdin para stdout, tente:

echo "header line" | cut -c 1-5 - input_file

(O traço é um atalho comum para stdin)

No entanto, isso também executará a operação de corte no stdin (resultando em uma linha de cabeçalho de "cabeçalho"). É difícil dizer se é isso que você quer ou não com a pergunta.

Sam Whited
fonte
13

O que você procura é:

{ echo "header line"; cut -c 1-5 input_file; } > output_file
  • Essa sintaxe não tem efeitos colaterais, pois os comandos são executados no shell atual, não um subshell
  • Há uma delimitação clara dos comandos que vão para o arquivo de saída
  • Escala bem, como você pode reescrevê-lo dessa maneira:

{
  echo "header line"
  cut -c 1-5 input_file
  ... # insert new commands here
} > output_file

Se você desejar que a saída de erro vá para o mesmo arquivo, você pode modificar a última linha dessa maneira:

} > output_file 2>&1

Agradecemos a Olivier Dulac por lembrar essa dica.

Se você deseja que alguma saída dentro do bloco vá para sua tela, você pode usar essa sintaxe:

{
  echo "header line"
  echo "this line doesn't go to output_file" > /dev/tty
  cut -c 1-5 input_file
} > output_file
jlliagre
fonte
+1: é mais claro e identifica claramente o que é redirecionado para onde. Para redirecionar tudo (incluindo msg de erro): { .... } > some_file 2>&1(vai "clobber" nome_ficheiro não clobber, mas acrescentar a ela em vez disso, basta alterar o. >Em um >>: { ... } >> some_file 2>&1)
Olivier Dulac
4

Apenas para completar as respostas, há exec.

exec > output_file
echo "header line"
cut -c 1-5 input_file
hildred
fonte
+1: em scripts é muito comum para usar este (nem tanto na linha de comando, como as coisas começam a ficar confusa ^^)
Olivier Dulac
Alguém poderia elaborar essa solução? Eu posso ver o que está fazendo, mas não entendo 'exec' nesse contexto. Além disso, parece que você precisa desativar novamente se o restante do script precisar de um stdout normal.
31413 Joe
1
quando você usa exec em um script sem um comando, é possível fazer o redirecionamento de E / S no exec que afeta o shell atual e todos os futuros filhos.
Hildred
Obrigado. Eu não vi isso antes. Eu acho que será muito útil.
Joe