Força a liberação de saída para um arquivo enquanto o script bash ainda está em execução


90

Eu tenho um pequeno script, que é chamado diariamente pelo crontab usando o seguinte comando:

/homedir/MyScript &> some_log.log

O problema com esse método é que some_log.log só é criado depois que o MyScript termina. Eu gostaria de liberar a saída do programa no arquivo enquanto ele está sendo executado para que eu pudesse fazer coisas como

tail -f some_log.log

e acompanhar o progresso, etc.


Precisamos de uma descrição - ou, se possível, um código - do que seu pequeno script faz exatamente ...
ChristopheD

7
Para descompactar scripts python, você pode usar "python -u". Para descompactar scripts perl, veja a resposta de Greg Hewgill abaixo. E assim por diante ...
Eloici

Se você pode editar o script, normalmente pode liberar o buffer de saída explicitamente dentro de um script, por exemplo em python com sys.stdout.flush().
drevicko

Respostas:


28

O próprio bash nunca gravará nenhuma saída em seu arquivo de log. Em vez disso, os comandos que ele invoca como parte do script irão, cada um individualmente, gravar a saída e liberar sempre que desejar. Portanto, a sua pergunta é realmente como forçar o esvaziamento dos comandos dentro do script bash, e isso depende do que eles são.


25
Eu realmente não entendo essa resposta.
Alfonso Santiago de

3
Para ter uma ideia melhor de por que a saída padrão se comporta assim, consulte stackoverflow.com/a/13933741/282728 . Uma versão curta - por padrão, se redirecionado para um arquivo, o stdout é totalmente armazenado em buffer; ele é gravado em um arquivo somente após uma descarga. Stderr não é - é escrito após cada '\ n'. Uma solução é usar o comando 'script' recomendado pelo usuário3258569 abaixo, para que o stdout seja liberado após cada final de linha.
Alex

2
Afirmando o óbvio, e dez anos depois, mas este é um comentário, não uma resposta, e não deveria ser a resposta aceita.
RealHandy

87

Eu encontrei uma solução para isso aqui . Usando o exemplo do OP, você basicamente executa

stdbuf -oL /homedir/MyScript &> some_log.log

e o buffer é liberado após cada linha de saída. Costumo combinar isso com nohupa execução de trabalhos longos em uma máquina remota.

stdbuf -oL nohup /homedir/MyScript &> some_log.log

Desta forma, o seu processo não é cancelado quando você sai.


1
Você poderia adicionar um link para alguma documentação para stdbuf? Com base neste comentário , parece que não está disponível em algumas distros. Você poderia esclarecer?
Ação judicial de Monica de

1
stdbuf -o ajusta o buffer stdout. As outras opções são -i e -e para stdin e stderr. L define o buffer de linha. Também é possível especificar o tamanho do buffer ou 0 para nenhum buffer.
Seppo Enarvi

5
esse link não está mais disponível.
john-jones

2
@NicHartley: stdbufé parte do GNU coreutils, a documentação pode ser encontrada em gnu.org
Thor

Caso ajude alguém, use export -f my_function e stdbuf -oL bash -c "my_function -args"se precisar executar uma função em vez de um script
Anônimo

29
script -c <PROGRAM> -f OUTPUT.txt

A chave é -f. Citação do man script:

-f, --flush
     Flush output after each write.  This is nice for telecooperation: one person
     does 'mkfifo foo; script -f foo', and another can supervise real-time what is
     being done using 'cat foo'.

Executado em segundo plano:

nohup script -c <PROGRAM> -f OUTPUT.txt

Uau! Uma solução que funciona busybox! (minha concha congela depois, mas tanto faz)
Victor Sergienko

9

Você pode usar teepara gravar no arquivo sem a necessidade de descarregar.

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null

2
Isso ainda armazena a saída, pelo menos no meu ambiente Ubuntu 18.04. O conteúdo eventualmente é gravado no arquivo de qualquer maneira, mas acho que o OP está pedindo um método onde eles possam monitorar o progresso com mais precisão antes de o arquivo terminar de ser escrito, e este método não permite isso mais do que o redirecionamento de saída faz.
mltsy

3

Isso não é uma função de bash, pois tudo o que o shell faz é abrir o arquivo em questão e, em seguida, passar o descritor de arquivo como a saída padrão do script. O que você precisa fazer é garantir que a saída seja eliminada do seu script com mais frequência do que você atualmente.

Em Perl, por exemplo, isso pode ser feito definindo:

$| = 1;

Veja perlvar para mais informações sobre isso.


2

O armazenamento em buffer da saída depende de como seu programa /homedir/MyScripté implementado. Se você descobrir que a saída está sendo armazenada em buffer, será necessário forçá-la em sua implementação. Por exemplo, use sys.stdout.flush () se for um programa Python ou use fflush (stdout) se for um programa C.


2

Isso ajudaria?

tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

Isso exibirá imediatamente entradas exclusivas do access.log usando o utilitário stdbuf .


O único problema é que stdbuf parece ser um utilitário antigo que não está disponível em novas distros.
Ondra Žižka

..nor in my busybox :(
Campa

Na verdade, tenho stdbufdisponível no Ubuntu no momento, não tenho certeza de onde consegui.
Ondra Žižka

Tenho stdbuf no Centos 7.5
Max

1
No Ubuntu 18.04, stdbuffaz parte de coreutilus(encontrado com apt-file search /usr/bin/stdbuf).
Rmano,

1

O problema aqui é que você tem que esperar que os programas que você executa a partir do seu script terminem seus trabalhos.
Se no seu script você executa o programa em segundo plano, você pode tentar algo mais.

Em geral, uma chamada para syncantes de sair permite liberar buffers do sistema de arquivos e pode ajudar um pouco.

Se no script você iniciar alguns programas em segundo plano ( &), você pode esperar que eles terminem antes de sair do script. Para se ter uma ideia de como ele pode funcionar você pode ver abaixo

#!/bin/bash
#... some stuffs ...
program_1 &          # here you start a program 1 in background
PID_PROGRAM_1=${!}   # here you remember its PID
#... some other stuffs ... 
program_2 &          # here you start a program 2 in background
wait ${!}            # You wait it finish not really useful here
#... some other stuffs ... 
daemon_1 &           # We will not wait it will finish
program_3 &          # here you start a program 1 in background
PID_PROGRAM_3=${!}   # here you remember its PID
#... last other stuffs ... 
sync
wait $PID_PROGRAM_1
wait $PID_PROGRAM_3  # program 2 is just ended
# ...

Uma vez que waitfunciona com tarefas e também com PIDnúmeros, uma solução preguiçosa deve ser colocada no final do script

for job in `jobs -p`
do
   wait $job 
done

Mais difícil é a situação se você executar algo que executa outra coisa em segundo plano porque você tem que pesquisar e esperar (se for o caso) o fim de todo o processo filho : por exemplo, se você executar um daemon provavelmente não é o caso esperar termine :-).

Nota:

  • wait $ {!} significa "esperar até que o último processo em segundo plano seja concluído" onde $!está o PID do último processo em segundo plano. Então, colocar wait ${!}logo depois program_2 &é equivalente a executar diretamente program_2sem enviá-lo em segundo plano com&

  • Com a ajuda de wait:

    Syntax    
        wait [n ...]
    Key  
        n A process ID or a job specification
    

1

Obrigado @user3258569, o script é talvez a única coisa que funciona busybox!

A casca estava congelando para mim depois disso, no entanto. Procurando a causa, encontrei estes grandes avisos vermelhos "não use em shells não interativos" na página de manual do script :

scripté projetado principalmente para sessões de terminal interativas. Quando stdin não é um terminal (por exemplo echo foo | script:), a sessão pode travar, porque o shell interativo dentro da sessão de script perde EOF e scriptnão tem ideia de quando fechar a sessão. Consulte a seção NOTAS para obter mais informações.

Verdadeiro. script -c "make_hay" -f /dev/null | grep "needle"estava congelando a casca para mim.

Camponês ao aviso, pensei que echo "make_hay" | scriptIRÁ passar um EOF, por isso tentei

echo "make_hay; exit" | script -f /dev/null | grep 'needle'

e funcionou!

Observe os avisos na página do manual. Isso pode não funcionar para você.


0

alternativa para stdbuf é awk '{print} END {fflush()}' eu gostaria que houvesse um bash embutido para fazer isso. Normalmente não deveria ser necessário, mas com versões mais antigas pode haver erros de sincronização do bash nos descritores de arquivo.


-2

Não sei se daria certo, mas que tal ligar sync?


1
syncé uma operação de sistema de arquivos de baixo nível e não está relacionada à saída em buffer no nível do aplicativo.
Greg Hewgill

2
syncgrava quaisquer buffers de sistema de arquivos sujos no armazenamento físico, se necessário. Isso é interno ao sistema operacional; os aplicativos executados no sistema operacional sempre têm uma visão coerente do sistema de arquivos, independentemente de os blocos de disco terem sido gravados ou não no armazenamento físico. Para a pergunta original, o aplicativo (script) provavelmente está armazenando em buffer a saída em um buffer interno ao aplicativo, e o SO nem saberá (ainda) que a saída está realmente destinada a ser gravada em stdout. Portanto, uma operação hipotética do tipo "sincronização" não seria capaz de "acessar" o script e extrair os dados.
Greg Hewgill

-2

Tive esse problema com um processo em segundo plano no Mac OS X usando o StartupItems. É assim que eu resolvo:

Se eu fizer sudo ps aux, posso ver que mytoolfoi lançado.

Descobri que (devido ao buffer), quando o Mac OS X é encerrado, mytoolnunca transfere a saída para o sedcomando. No entanto, se eu executar sudo killall mytool, mytooltransfere a saída para o sedcomando. Portanto, adicionei um stopcaso ao StartupItemsque é executado quando o Mac OS X é encerrado:

start)
    if [ -x /sw/sbin/mytool ]; then
      # run the daemon
      ConsoleMessage "Starting mytool"
      (mytool | sed .... >> myfile.txt) & 
    fi
    ;;
stop)
    ConsoleMessage "Killing mytool"
    killall mytool
    ;;

Essa realmente não é uma boa resposta, Freeman, já que é muito específica para o seu ambiente. O OP deseja monitorar a saída, não eliminá-la.
Gray

-3

bem, goste ou não, é assim que o redirecionamento funciona.

No seu caso, a saída (significando que o seu script foi concluído) do seu script redirecionada para esse arquivo.

O que você deseja fazer é adicionar esses redirecionamentos em seu script.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.