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

问题描述 投票:59回答:13

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

/homedir/MyScript &> some_log.log

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

tail -f some_log.log

并跟踪进度等

bash file flush
13个回答
25
投票

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


0
投票

替代stdbuf是awk '{print} END {fflush()}'我希望有一个bash内置来做到这一点。通常它不是必需的,但对于旧版本,文件描述符上可能存在bash同步错误。


-1
投票

我不知道它是否会起作用,但是如何调用sync呢?


-1
投票

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

如果我制作sudo ps aux,我可以看到mytool已经发布。

我发现(由于缓冲)当Mac OS X关闭时,mytool永远不会将输出传输到sed命令。但是,如果我执行sudo killall mytool,那么mytool会将输出传输到sed命令。因此,我在stop中添加了一个StartupItems案例,该案例在Mac OS X关闭时执行:

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

-2
投票

不管你喜欢与否,这都是重定向的工作原理。

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

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


66
投票

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

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

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

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

这样,当您注销时,您的进程不会被取消。


21
投票

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

9
投票

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

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

3
投票

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

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

$| = 1;

有关详细信息,请参阅perlvar


2
投票

这会有帮助吗?

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

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


2
投票

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


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

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

注意:

  • wait $ {!}表示“等到最后一个后台进程完成”,其中$!是最后一个后台进程的PID。所以把wait ${!}放在program_2 &之后等同于直接执行program_2而不用&在背景中发送它
  • wait的帮助下: Syntax wait [n ...] Key n A process ID or a job specification

1
投票

谢谢@user3258569,脚本可能是唯一适用于busybox的东西!

不过,贝壳在它之后冻结了。寻找原因,我在script manual page发现了这些大红色警告“不要在非交互式贝壳中使用”:

script主要用于交互式终端会话。当stdin不是终端(例如:echo foo | script)时,会话可以挂起,因为脚本会话中的交互式shell错过了EOF,script不知道何时关闭会话。有关更多信息,请参阅NOTES部分。

真正。 script -c "make_hay" -f /dev/null | grep "needle"为我冻结了壳。

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

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

它工作了!

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

© www.soinside.com 2019 - 2024. All rights reserved.