102

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

/homedir/MyScript &> some_log.log

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

tail -f some_log.log

并跟踪进度等。

4

13 回答 13

100

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

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

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

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

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

于 2015-06-15T12:27:56.993 回答
36
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
于 2014-01-31T18:59:28.643 回答
31

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

于 2009-09-15T22:33:40.593 回答
8

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

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null
于 2013-04-13T10:41:48.080 回答
3

这不是 的函数bash,因为 shell 所做的只是打开有问题的文件,然后将文件描述符作为脚本的标准输出传递。您需要做的是确保从您的脚本中刷新输出的频率比您现在要高。

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

$| = 1;

有关这方面的更多信息,请参见perlvar

于 2009-09-15T22:32:15.627 回答
3

这会有帮助吗?

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

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

于 2010-12-23T17:18:51.933 回答
2

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

于 2017-04-09T13:21:23.237 回答
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
    
于 2014-06-26T06:37:50.677 回答
1

谢谢@user3258569,脚本可能是唯一有效的东西busybox

不过,在它之后,贝壳对我来说是冰冻的。寻找原因,我在脚本手册页中发现了这些红色的大警告“不要在非交互式 shell 中使用” :

script主要是为交互式终端会话而设计的。当 stdin 不是终端(例如:)时echo foo | script,会话可能会挂起,因为脚本会话中的交互式 shell 错过了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'

它奏效了!

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

于 2018-07-06T23:51:16.707 回答
0

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

于 2019-01-25T02:42:48.490 回答
-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
    ;;
于 2010-05-29T20:41:39.353 回答
-4

我不知道它是否会起作用,但是打电话sync呢?

于 2009-09-15T22:41:46.527 回答
-4

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

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

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

于 2009-09-15T22:48:17.730 回答