在bash脚本仍在运行时强制将输出刷新到文件

时间:2009-09-15 22:26:19

标签: bash file flush

我有一个小脚本,crontab每天使用以下命令调用它:

/homedir/MyScript &> some_log.log

此方法的问题是some_log.log仅在MyScript完成后创建。我希望在程序运行时将程序的输出刷新到文件中,这样我就可以执行

之类的操作
tail -f some_log.log

并跟踪进度等。

13 个答案:

答案 0 :(得分:60)

我找到了这个here的解决方案。使用OP的例子你基本上运行

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

然后在每行输出后刷新缓冲区。我经常将它与nohup结合起来在远程机器上运行长作业。

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

这样,当您退出时,您的流程不会被取消。

答案 1 :(得分:25)

bash本身永远不会将任何输出写入您的日志文件。相反,它作为脚本的一部分调用的命令将分别编写输出并在它们感觉到时刷新。所以你的问题实际上是如何强制bash脚本中的命令刷新,这取决于它们是什么。

答案 2 :(得分:18)

script -c <PROGRAM> -f OUTPUT.txt

键是-f。从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'.

在后台运行:

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

答案 3 :(得分:8)

您可以使用tee写入文件,而无需刷新。

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

答案 4 :(得分:3)

这不是bash的函数,因为所有shell都会打开相关文件,然后将文件描述符作为脚本的标准输出传递。您需要做的是确保输出从脚本中刷新比当前更频繁。

例如,在Perl中,可以通过设置:

来完成
$| = 1;

有关详细信息,请参阅perlvar

答案 5 :(得分:2)

这会有帮助吗?

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

这将立即显示来自access.log的唯一条目 http://www.pixelbeat.org/programming/stdio_buffering/stdbuf-man.html

答案 6 :(得分:2)

缓冲输出取决于程序/homedir/MyScript的实现方式。如果您发现输出已缓冲,则必须在实现中强制它。例如,如果是python程序,请使用sys.stdout.flush();如果是C程序,请使用fflush(stdout)。

答案 7 :(得分:1)

如何发现here问题是你必须等待从脚本运行的程序完成他们的工作。
如果您在脚本中运行后台中的程序,则可以尝试更多操作。

通常,在退出之前调用sync允许刷新文件系统缓冲区并且可以帮助一点。

如果您在脚本中启动了后台&)中的某些程序,则可以在退出脚本之前wait完成这些程序。要了解它如何运作,您可以在下面看到

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

如果您运行在后台运行其他内容的东西会更加困难,因为您必须搜索并等待(如果是这种情况)所有进程的结束:例如,如果你运行守护程序可能不是等待它完成的情况:-)。

注意:

  • 等待$ {!}表示&#34;等到最后一个后台进程完成&#34;其中$!是最后一个后台进程的PID。因此,wait ${!}之后的program_2 &等同于直接执行program_2,而不在后台发送&

  • wait的帮助下:

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

答案 8 :(得分:1)

感谢@user3258569,脚本可能是唯一可以在busybox中使用的东西!

不过,壳对我来说冻结了。在寻找原因的过程中,我在script manual page中发现了这些大的红色警告“请勿在非交互式shell中使用”:

  

script主要用于交互式终端会话。什么时候          stdin不是终端(例如:echo foo | script),则          会话可以挂起,因为脚本内的交互式外壳          会话未完成 EOF ,并且script不知道何时关闭会话。          有关更多信息,请参见注释部分。

是的。 script -c "make_hay" -f /dev/null | grep "needle"为我冻结了外壳。

与警告相反,我认为echo "make_hay" | script将通过EOF,所以我尝试了

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

成功了!

请注意手册页中的警告。这可能对您不起作用。

答案 9 :(得分:0)

替代stdbuf的是awk '{print} END {fflush()}' 我希望内置一个bash来执行此操作。 通常没有必要,但是对于较旧的版本,文件描述符上可能会出现bash同步错误。

答案 10 :(得分:-1)

我不知道它是否有效,但是如何调用sync

答案 11 :(得分:-1)

我使用StartupItems在Mac OS X中使用后台进程遇到此问题。这就是我解决它的方法:

如果我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
    ;;

答案 12 :(得分:-2)

是否喜欢这是重定向的工作原理。

在您的情况下,脚本的输出(意味着您的脚本已完成)重定向到该文件。

您要做的是在脚本中添加这些重定向。