como gerar texto para tela e arquivo dentro de um script de shell?

49

Atualmente, tenho um script de shell que registra mensagens em um arquivo de log como este:

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

Ao executar esse script, não há saída para a tela e, como estou me conectando ao servidor via putty, tenho que abrir outra conexão e fazer "tail -f log_file_path.log", porque não consigo terminar a execução script e eu quero ver a saída em tempo real.

Obviamente, o que eu quero é que as mensagens de texto sejam impressas na tela e no arquivo, mas eu gostaria de fazê-lo em uma linha, não em duas linhas, uma das quais não tem redirecionamento para o arquivo.

Como conseguir isso?

jurchiks
fonte

Respostas:

71

Isso funciona:

command | tee -a "$log_file"

teesalva a entrada em um arquivo (use -apara acrescentar em vez de substituir) e copia a entrada para a saída padrão também.

l0b0
fonte
8

Você pode usar um documento aqui e. forneça um modelo de coletor geral eficiente e compatível com POSIX.

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

Ao abrir o heredoc, você sinaliza o shell com um token de entrada IOHERE, que deve redirecionar sua entrada para o descritor de arquivo especificado até encontrar o outro extremo do seu token limitador. Eu olhei em volta, mas não vi muitos exemplos do uso do número fd de redirecionamento, como mostrei acima em combinação com o operador heredoc, embora seu uso seja claramente especificado nas diretrizes básicas de comando shell do POSIX. A maioria das pessoas apenas aponta para stdin e dispara, mas acho que os scripts de fornecimento dessa maneira podem manter o stdin livre e os aplicativos constituintes se queixam de caminhos de E / S bloqueados.

O conteúdo do heredoc é transmitido para o descritor de arquivo que você especificar, que por sua vez é interpretado como código shell e executado pelo. embutido, mas não sem especificar um caminho específico para. . Se o caminho / proc / self causar problemas, tente / dev / fd / n ou / proc / $$. Este mesmo método funciona em tubos, a propósito:

cat ./*.sh | . /dev/stdin

Provavelmente é pelo menos tão imprudente quanto parece. Você pode fazer o mesmo com sh, é claro, mas o objetivo do. com um tubo anônimo padrão.

De qualquer forma, como você provavelmente já percebeu, ainda não respondi sua pergunta. Mas se você pensar sobre isso, da mesma forma que o heredoc transmite todo o seu código para .'s in, ele também fornece um ponto de vista único e simples:

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

Portanto, todo o stdout do terminal de qualquer código executado no seu heredoc é canalizado para fora. naturalmente e pode ser facilmente arrancado de um único tubo. Incluí a chamada de gato sem buffer porque não tenho certeza sobre a direção atual do stdout, mas é provavelmente redundante (quase certamente é como está escrito de qualquer maneira) e o pipeline provavelmente pode terminar no ponto.

Você também pode questionar a cotação de barra invertida ausente no segundo exemplo. Esta parte é importante para entender antes de você entrar e pode dar algumas idéias sobre como ela pode ser usada. Um limitador heredoc citado (até agora usamos IOHERE e EOIN, e o primeiro citei com uma barra invertida, embora aspas 'simples' ou 'duplas' sirvam ao mesmo objetivo) impedirão o shell de executar qualquer expansão de parâmetro no conteúdo, mas um limitador não citado deixará seu conteúdo aberto à expansão. As consequências disso quando o seu heredoc é. as fontes são dramáticas:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

Como não citei o limitador de heredoc, o shell expandiu o conteúdo à medida que o lia e antes de servir o descritor de arquivo resultante. executar. Isso basicamente resultou na análise dos comandos duas vezes - os expansíveis de qualquer maneira. Como a barra invertida citou a expansão do parâmetro $ vars, o shell ignorou sua declaração na primeira passagem e apenas retirou a barra invertida para que todo o conteúdo expandido da impressão pudesse ser avaliado por nulo quando. originou o script na segunda passagem.

Essa funcionalidade é basicamente exatamente o que o perigoso eval shell interno pode fornecer, mesmo que a citação seja muito mais fácil de manusear em um heredoc do que com eval, e possa ser igualmente perigosa. A menos que você planeje com cuidado, provavelmente é melhor citar o limitador "EOF" como uma questão de hábito. Apenas dizendo.

EDIT: Eh, eu estou olhando para trás e pensando que é um pouco demais. Se tudo o que você precisa fazer é concatenar várias saídas em um tubo, o método mais simples é usar um:

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

Os curlies tentam executar o conteúdo no shell atual, enquanto os parens diminuem automaticamente. Ainda assim, qualquer um pode lhe dizer isso e, pelo menos na minha opinião, o. A solução heredoc é uma informação muito mais valiosa, especialmente se você quiser entender como o shell realmente funciona.

Diverta-se!

mikeserv
fonte
3

Encontrei esta resposta enquanto procurava modificar um script de instalação para escrever um log de instalação.

Meu script já está cheio de instruções de eco como:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

E eu não queria uma instrução tee para executá-la (ou outro script para chamar a existente com tee), então escrevi o seguinte:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

Portanto, agora no meu script original, eu posso mudar 'echo' para 'echolog', onde quero a saída em um arquivo de log.

AndruWitta
fonte