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


89

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

/homedir/MyScript &> some_log.log

此方法的问题在于,仅在MyScript完成后才创建some_log.log。我想在程序运行时将程序的输出刷新到文件中,以便执行以下操作

tail -f some_log.log

并跟踪进度等。


我们需要有一个描述-或者,如果你的小脚本做究竟是什么?可能的代码-
ChristopheD

7
要取消缓冲python脚本,可以使用“ python -u”。要取消缓冲perl脚本,请参阅下面的Greg Hewgill答复。依此类推...
Eloici 2014年

如果您可以编辑脚本,通常可以在脚本中显式刷新输出缓冲区,例如使用sys.stdout.flush()
drevicko

Answers:


28

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


23
我真的不明白这个答案。
阿方索·圣地亚哥

2
要更好地了解标准输出为何如此运行,请查看stackoverflow.com/a/13933741/282728。简短版本-默认情况下,如果重定向到文件,则stdout会被完全缓冲;仅在刷新后才将其写入文件。Stderr不是-它写在每个'\ n'之后。一种解决方案是使用下面的user3258569建议的'script'命令,以使stdout在每一行结束后都被刷新。
Alex

十年之后,陈述了明显的答案,但这只是评论,而不是答案,它不应该是公认的答案。
RealHandy

84

我在这里找到了解决方案。使用OP的示例,您基本上可以运行

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

然后在输出的每一行之后刷新缓冲区。我经常将其与nohup在远程计算机上运行长时间作业结合在一起。

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

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


1
您可以添加指向某些文档的链接stdbuf吗?根据此评论,似乎在某些发行版中不可用。你能澄清一下吗?
基金莫妮卡的诉讼

1
stdbuf -o调整标准输出缓冲。对于stdin和stderr,其他选项是-i和-e。L设置行缓冲。还可以指定缓冲区大小,或者指定0表示没有缓冲。
Seppo Enarvi '16

5
该链接不再可用。
Fzs2

2
@NicHartley:stdbuf是GNU的coreutils的一部分,文档可以在gnu.org发现
雷神

如果它对任何人都有帮助,请使用 export -f my_function,然后stdbuf -oL bash -c "my_function -args"在需要运行功能而不是脚本的情况下使用
匿名

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!(之后我的外壳会冻结,但是无论如何)
Victor Sergienko '18年

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的实现方式。如果发现输出正在缓冲,则必须在实现中强制使用它。例如,如果是python程序,请使用sys.stdout.flush();如果是C程序,请使用fflush(stdout)。


2

这会有所帮助吗?

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

这将立即使用stdbuf实用程序显示access.log中的唯一条目。


唯一的麻烦是stdbuf似乎是一些旧的实用程序,而新发行版中没有。
OndraŽižka2010年

..nor在我的繁忙信箱中:(
坎帕

实际上,我目前stdbuf在Ubuntu中可用,不确定我从哪里得到的。
OndraŽižka'18

我已经stdbuf于CentOS 7.5
马克斯

1
在Ubuntu 18.04中,stdbuf是的一部分coreutilus(位于apt-file search /usr/bin/stdbuf)。
Rmano '18 -10-2

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

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

注意:

  • 等待$ {!}表示“等待直到最后一个后台进程完成”,其中$!是最后一个后台进程的PID。所以放在wait ${!}后面program_2 &就等于直接执行program_2而无需在后台发送&

  • 来自wait

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

1

谢谢@user3258569,脚本可能是唯一起作用的东西busybox

不过,壳对我来说冻结了。寻找原因,我在脚本手册页中找到了这些大的红色警告“请勿在非交互式外壳中使用” :

script主要用于交互式终端会话。如果stdin不是终端(例如:)echo foo | script,则会话可以挂起,因为脚本会话中的交互式shell会错过EOFscript并且不知道何时关闭会话。有关更多信息,请参见“注意”部分。

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

反对警告,我以为会echo "make_hay" | script通过EOF,所以我尝试了

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

而且有效!

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


0

awk '{print} END {fflush()}' 我希望stdbuf的替代品是内置bash来实现的。通常没有必要,但是对于较旧的版本,文件描述符上可能存在bash同步错误。


-2

我不知道是否可行,但是打电话sync呢?


1
sync是低级文件系统操作,与应用程序级别的缓冲输出无关。
Greg Hewgill

2
sync如有必要,将所有脏文件系统缓冲区写入物理存储。这是操作系统内部的;无论磁盘块是否已写入物理存储,运行在OS之上的应用程序始终会看到文件系统的一致视图。对于最初的问题,应用程序(脚本)可能正在将输出缓冲在应用程序内部的缓冲区中,并且操作系统甚至都不知道(但)输出实际上已注定要写入stdout。因此,假设的“同步”类型操作将无法“进入”脚本并提取数据。
Greg Hewgill 09年

-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
    ;;

Freeman确实不是一个很好的答案,因为它非常适合您的环境。OP希望监视输出而不是杀死它。
灰色

-3

不管喜欢与否,这就是重定向的原理。

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.