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


89

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

/homedir/MyScript &> some_log.log

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

tail -f some_log.log

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


작은 스크립트가 정확히 수행하는 작업에 대한 설명 (또는 가능한 경우 코드)이 필요합니다.
ChristopheD

7
파이썬 스크립트의 버퍼링을 해제하려면 "python -u"를 사용할 수 있습니다. Perl 스크립트의 버퍼링을 해제하려면 아래의 Greg Hewgill 응답을 참조하십시오. 기타 등등 ...
Eloici 2014 년

스크립트를 편집 할 수있는 경우 일반적으로 스크립트 내에서 명시 적으로 출력 버퍼를 플러시 할 수 있습니다 (예 : sys.stdout.flush().
drevicko

답변:


28

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


23
나는이 대답을 정말로 이해하지 못한다.
Alfonso Santiago

2
표준 출력이 이와 같이 작동하는 이유를 더 잘 이해하려면 stackoverflow.com/a/13933741/282728을 확인하십시오 . 짧은 버전 — 기본적으로 파일로 리디렉션되면 stdout이 완전히 버퍼링됩니다. 플러시 후에 만 ​​파일에 기록됩니다. Stderr는 그렇지 않습니다. 매 '\ n'뒤에 기록됩니다. 한 가지 해결책은 아래 user3258569가 권장하는 'script'명령을 사용하여 모든 줄 끝마다 stdout을 플러시하는 것입니다.
Alex

명백한 것을 말하고 10 년 후, 그러나 이것은 답변이 아니라 코멘트이며, 받아 들여지는 대답이되어서는 안됩니다.
RealHandy

84

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

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

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

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

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


1
에 대한 일부 문서에 대한 링크를 추가 할 수 stdbuf있습니까? 이 의견 에 따르면 일부 배포판에서는 사용할 수없는 것 같습니다. 명확히 해 주시겠습니까?
기금 모니카의 소송

1
stdbuf -o는 stdout 버퍼링을 조정합니다. 다른 옵션은 stdin 및 stderr의 경우 -i 및 -e입니다. L은 라인 버퍼링을 설정합니다. 버퍼 크기를 지정하거나 버퍼링이없는 경우 0을 지정할 수도 있습니다.
Seppo Enarvi

5
해당 링크는 더 이상 사용할 수 없습니다.
Fzs2

2
@NicHartley : stdbufGNU의로 coreutils의 일부입니다 문서는 gnu.org에서 찾을 수 있습니다
토르

누구에게나 도움이되는 경우 사용 export -f my_function 하고 stdbuf -oL bash -c "my_function -args"스크립트 대신 함수를 실행해야하는 경우
Anonymous

28
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

와! 에서 작동하는 솔루션 busybox! (내 쉘은 이후 정지하지만, 무엇이든)
빅터 Sergienko

9

tee플러시 할 필요없이를 사용하여 파일에 쓸 수 있습니다 .

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

2
이것은 적어도 내 Ubuntu 18.04 환경에서 여전히 출력을 버퍼링합니다. 결국 내용은 어느 쪽이든 파일에 기록되지만 OP는 파일 쓰기가 완료되기 전에 진행 상황을보다 정확하게 모니터링 할 수있는 방법을 요구하고 있다고 생각하며이 방법은 출력 리디렉션 이상을 허용하지 않습니다. 그렇습니다.
mltsy

3

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

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

$| = 1;

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


2

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


2

이것이 도움이 될까요?

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

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


유일한 문제는 stdbuf가 새 배포판에서 사용할 수없는 오래된 유틸리티 인 것처럼 보인다는 것입니다.
Ondra Žižka

..nor in my busybox :(
Campa

실제로 나는 현재 stdbuf우분투에서 사용할 수 있지만 어디서 구했는지 확실하지 않습니다.
Ondra Žižka

Centos 7.5에 stdbuf가 있습니다
Max

1
Ubuntu 18.04에서는 (에서 발견됨 )의 stdbuf일부입니다 . coreutilusapt-file search /usr/bin/stdbuf
Rmano

1

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

일반적으로 전화 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 $ {!}는 마지막 백그라운드 프로세스 $!의 PID 인 "마지막 백그라운드 프로세스가 완료 될 때까지 대기"를 의미 합니다. 따라서 wait ${!}바로 뒤에 넣는 program_2 &것은 직접 실행하는 것과 같습니다.program_2 것은 백그라운드로 보내지 않고&

  • 의 도움에서 wait:

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

1

감사합니다 @user3258569, 스크립트는 아마도 작동하는 유일한 것입니다 busybox!

그래도 껍질이 얼어 붙었다. 원인을 찾아 보니 스크립트 매뉴얼 페이지 에서 "비대화 형 셸에서 사용하지 마십시오"라는 큰 빨간색 경고를 발견했습니다 .

script주로 대화 형 터미널 세션 용으로 설계되었습니다. 표준 입력 (예 : 터미널이 아닌 경우 echo foo | script) 스크립트 세션 내에서 대화 형 쉘이 그리워하기 때문에, 다음 세션이 중단 될 수 있습니다 EOF를 하고 script때 세션을 종료하는 단서가 없습니다. 자세한 내용은 참고 섹션을 참조하십시오.

진실. script -c "make_hay" -f /dev/null | grep "needle"나를 위해 껍데기를 얼리고 있었다.

경고에 반하여 echo "make_hay" | scriptEOF를 통과 할 것이라고 생각 했기 때문에

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

그리고 작동했습니다!

매뉴얼 페이지의 경고에 유의하십시오. 이것은 당신을 위해 작동하지 않을 수 있습니다.


0

stdbuf의 대안 awk '{print} END {fflush()}' 은 이것을 수행하기 위해 bash가 내장되어 있기를 바랍니다. 일반적으로 필요하지 않지만 이전 버전에서는 파일 설명자에 bash 동기화 버그가있을 수 있습니다.


-2

작동할지 모르겠지만 전화는 sync어떻습니까?


1
sync저수준 파일 시스템 작업이며 응용 프로그램 수준에서 버퍼링 된 출력과 관련이 없습니다.
Greg Hewgill

2
sync필요한 경우 더티 파일 시스템 버퍼를 물리적 스토리지에 기록합니다. 이것은 OS 내부입니다. OS 위에서 실행되는 애플리케이션은 디스크 블록이 물리적 스토리지에 기록되었는지 여부에 관계없이 항상 파일 시스템의 일관된보기를 볼 수 있습니다. 원래 질문에 대해 응용 프로그램 (스크립트)은 아마도 응용 프로그램 내부의 버퍼에 출력을 버퍼링하고 있으며 OS는 출력이 실제로 stdout에 기록 될 예정임을 (아직) 알지 못할 것입니다. 따라서 가상의 "동기화"유형 작업은 스크립트에 "접근"하여 데이터를 가져올 수 없습니다.
Greg Hewgill

-2

Mac OS X의 백그라운드 프로세스에서이 문제가 발생했습니다. StartupItems . 이것이 내가 그것을 해결하는 방법입니다.

내가 만들면 시작 sudo ps aux되는 것을 볼 수 있습니다 mytool.

버퍼링으로 인해 Mac OS X가 종료 mytool되면 출력이 sed명령으로 전송되지 않습니다 . 내가 실행하는 경우에는 sudo killall mytool다음 mytool받는 출력 전송 sed명령을 사용합니다. 따라서 Mac OS X가 종료 될 때 실행 되는에 stop케이스를 추가했습니다 StartupItems.

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

이것은 당신의 환경에 매우 구체적이기 때문에 정말 좋은 대답이 아닙니다. OP는 출력을 모니터링하고 싶어하지 않습니다.
Gray

-3

좋든 싫든 이것이 리디렉션이 작동하는 방식입니다.

귀하의 경우 스크립트의 출력 (스크립트가 완료되었음을 의미)이 해당 파일로 리디렉션됩니다.

원하는 것은 스크립트에 이러한 리디렉션을 추가하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.