Program Club

bash 스크립트가 계속 실행되는 동안 출력을 파일로 강제 플러시

proclub 2020. 11. 4. 20:58
반응형

bash 스크립트가 계속 실행되는 동안 출력을 파일로 강제 플러시


다음 명령을 사용하여 crontab에서 매일 호출하는 작은 스크립트가 있습니다.

/homedir/MyScript &> some_log.log

이 방법의 문제점은 myScript가 완료된 후에 만 ​​some_log.log가 생성된다는 것입니다. 프로그램이 실행되는 동안 프로그램의 출력을 파일로 플러시하여 다음과 같은 일을 할 수 있습니다.

tail -f some_log.log

진행 상황 등을 추적합니다.


bash 자체는 실제로 로그 파일에 출력을 쓰지 않습니다. 대신 스크립트의 일부로 호출하는 명령은 각각 개별적으로 출력을 작성하고 원할 때마다 플러시합니다. 따라서 귀하의 질문은 실제로 bash 스크립트 내에서 명령을 강제로 플러시하는 방법이며, 그것은 그것이 무엇인지에 달려 있습니다.


여기에 대한 해결책을 찾았 습니다 . OP의 예제를 사용하여 기본적으로 실행합니다.

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

그런 다음 버퍼는 각 출력 라인 후에 플러시됩니다. 나는 종종 이것을 nohup원격 시스템에서 긴 작업을 실행하기 위해 결합 합니다.

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

이렇게하면 로그 아웃 할 때 프로세스가 취소되지 않습니다.


script -c <PROGRAM> -f OUTPUT.txt

키는 -f입니다. 맨 스크립트에서 인용 :

-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'.

백그라운드에서 실행:

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

tee플러시하지 않고도 파일에 쓸 수 있습니다 .

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

bash셸이 수행하는 모든 작업은 문제의 파일을 연 다음 파일 설명자를 스크립트의 표준 출력으로 전달하는 것이므로 이것은의 함수가 아닙니다 . 해야 할 일은 현재보다 더 자주 스크립트에서 출력이 플러시되는지 확인 하는 것입니다.

예를 들어 Perl에서는 다음을 설정하여 수행 할 수 있습니다.

$| = 1;

이에 대한 자세한 내용 perlvar참조하십시오 .


출력 버퍼링은 프로그램 /homedir/MyScript구현 방법에 따라 다릅니다 . 출력이 버퍼링되는 것을 발견하면 구현에서이를 강제해야합니다. 예를 들어, 파이썬 프로그램 인 경우 sys.stdout.flush ()를 사용하고 C 프로그램 인 경우 fflush (stdout)를 사용합니다.


이것이 도움이 될까요?

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

그러면 stdbuf 유틸리티를 사용하여 access.log의 고유 항목이 즉시 표시됩니다 .


방법은 발견 여기에 문제가 당신이 당신의 스크립트에서 실행되는 프로그램이 자신의 작업을 완료 것을 기다릴 필요가 있다는 것입니다.
스크립트에서 백그라운드 에서 프로그램을 실행 하면 더 많은 것을 시도 할 수 있습니다.

일반적으로 sync종료하기 전에를 호출하면 파일 시스템 버퍼를 플러시 할 수 있으며 약간의 도움이 될 수 있습니다.

스크립트에서 일부 프로그램을 백그라운드 ( &) 에서 시작하는 경우 스크립트를 종료하기 전에 완료 때까지 기다릴 수 있습니다 . 어떻게 작동하는지에 대한 아이디어를 얻으려면 아래에서 볼 수 있습니다.

#!/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
# ...

wait작업과 PID숫자 로 작동 하기 때문에 게으른 해결책은 스크립트 끝에 넣어야합니다.

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

더 어려운 것은 모든 자식 프로세스 의 끝을 검색하고 기다려야하기 때문에 백그라운드에서 다른 것을 실행하는 경우의 상황 입니다. 예를 들어 데몬 을 실행하는 경우에는 그렇지 않을 수 있습니다. 완료되기를 기다리려면 :-).

노트 :

  • wait ${!} means "wait till the last background process is completed" where $! is the PID of the last background process. So to put wait ${!} just after program_2 & is equivalent to execute directly program_2 without sending it in background with &

  • From the help of wait:

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

Thanks @user3258569, script is maybe the only thing that works in busybox!

The shell was freezing for me after it, though. Looking for the cause, I found these big red warnings "don't use in a non-interactive shells" in script manual page:

script is primarily designed for interactive terminal sessions. When stdin is not a terminal (for example: echo foo | script), then the session can hang, because the interactive shell within the script session misses EOF and script has no clue when to close the session. See the NOTES section for more information.

True. script -c "make_hay" -f /dev/null | grep "needle" was freezing the shell for me.

Countrary to the warning, I thought that echo "make_hay" | script WILL pass a EOF, so I tried

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

and it worked!

Note the warnings in the man page. This may not work for you.


alternative to stdbuf is awk '{print} END {fflush()}' I wish there were a bash builtin to do this. Normally it shouldn't be necessary, but with older versions there might be bash synchronization bugs on file descriptors.


I don't know if it would work, but what about calling sync?


I had this problem with a background process in Mac OS X using the StartupItems. This is how I solve it:

If I make sudo ps aux I can see that mytool is launched.

I found that (due to buffering) when Mac OS X shuts down mytool never transfers the output to the sed command. However, if I execute sudo killall mytool, then mytool transfers the output to the sed command. Hence, I added a stop case to the StartupItems that is executed when Mac OS X shuts down:

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

well like it or not this is how redirection works.

In your case the output (meaning your script has finished) of your script redirected to that file.

What you want to do is add those redirections in your script.

참고URL : https://stackoverflow.com/questions/1429951/force-flushing-of-output-to-a-file-while-bash-script-is-still-running

반응형