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

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

/homedir/MyScript &> some_log.log 

O problema com esse método é que o some_log.log é criado somente após o término do MyScript. Eu gostaria de liberar a saída do programa no arquivo enquanto ele está em execução para que eu possa fazer coisas como

 tail -f some_log.log 

e acompanhar o progresso, etc.

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 serão gravados individualmente e liberados sempre que quiserem. Então sua pergunta é realmente como forçar os comandos dentro do script bash para liberar, e isso depende do que eles são.

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

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

e, em seguida, o buffer fica liberado após cada linha de saída. Costumo combinar isso com nohup para executar trabalhos longos em uma máquina remota.

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

Desta forma, o seu processo não será cancelado quando você sair.

script -c -f OUTPUT.txt

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

  -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  -f OUTPUT.txt 

Você pode usar o tee para gravar no arquivo sem a necessidade de liberar.

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

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

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

 $| = 1; 

Veja perlvar para mais informações sobre isso.

Isso ajudaria?

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

Isso exibirá imediatamente inputs exclusivas do access.log
http://www.pixelbeat.org/programming/stdio_buffering/stdbuf-man.html

Como se viu aqui, o problema é que você tem que esperar que os programas que você executa a partir do seu script terminem seus trabalhos.
Se no seu script você executar o programa em segundo plano, poderá tentar algo mais.

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

Se no script você iniciar alguns programas em segundo plano ( & ), poderá esperar que eles terminem antes de sair do script. Para ter uma ideia de como isso funciona, 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 wait funciona com trabalhos, bem como com números PID , uma solução preguiçosa deve ser colocar no final do script

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

Mais difícil é a situação se você executar algo que execute algo em segundo plano, porque você tem que procurar 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 que termine :-).

Nota:

  • wait $ {!} significa “espere até que o último processo em segundo plano seja concluído”, em que $! é o PID do último processo em segundo plano. Então, para colocar em wait ${!} Logo após o program_2 & é equivalente a executar diretamente o program_2 sem enviá-lo em background com &

  • A partir da ajuda de wait :

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

Buffer de saída depende de como o seu programa /homedir/MyScript é implementado. Se você achar 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 em C.

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

A casca estava congelando para mim depois disso, no entanto. Procurando pela causa, encontrei esses 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 session pode travar, porque o shell interativo dentro da session de script perde EOF e o script não tem idéia de quando fechar a session. Veja a seção NOTAS para mais informações.

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

Rural ao aviso, pensei que echo "make_hay" | script echo "make_hay" | script vai passar um EOF, então eu tentei

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

e funcionou!

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

Eu não sei se funcionaria, mas que tal chamar a sync ?

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

Se eu fizer o sudo ps aux , posso ver que o mytool é iniciado.

Descobri que (devido ao armazenamento em buffer) quando o Mac OS X é desligado, o mytool nunca transfere a saída para o comando sed . No entanto, se eu executar o sudo killall mytool , o mytool transferirá a saída para o comando sed . Portanto, adicionei um caso de stop aos StartupItems que é executado quando o Mac OS X é desligado:

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

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

No seu caso, a saída (o que significa que o seu script terminou) do seu script redirecionado para esse arquivo.

O que você quer fazer é adicionar esses redirecionamentos ao seu script.