我有一个小脚本,每天由 crontab 使用以下命令调用:
/homedir/MyScript &> some_log.log
这种方法的问题是 some_log.log 仅在 MyScript 完成后创建。我想在程序运行时将程序的输出刷新到文件中,这样我就可以做类似的事情
tail -f some_log.log
并跟踪进度等。
我在这里找到了解决方案。使用 OP 的示例,您基本上可以运行
stdbuf -oL /homedir/MyScript &> some_log.log
然后在每行输出后刷新缓冲区。我经常将它与nohup
在远程机器上运行长时间的作业结合起来。
stdbuf -oL nohup /homedir/MyScript &> some_log.log
这样,当您注销时,您的进程不会被取消。
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
bash 本身永远不会真正将任何输出写入您的日志文件。相反,它作为脚本的一部分调用的命令将各自单独写入输出并在需要时刷新。所以你的问题实际上是如何强制 bash 脚本中的命令刷新,这取决于它们是什么。
您可以使用tee
写入文件而无需刷新。
/homedir/MyScript 2>&1 | tee some_log.log > /dev/null
这不是 的函数bash
,因为 shell 所做的只是打开有问题的文件,然后将文件描述符作为脚本的标准输出传递。您需要做的是确保从您的脚本中刷新输出的频率比您现在要高。
例如,在 Perl 中,这可以通过设置来完成:
$| = 1;
有关这方面的更多信息,请参见perlvar。
这会有帮助吗?
tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq
这将使用stdbuf 实用程序立即显示来自 access.log 的唯一条目。
输出的缓冲取决于你的程序/homedir/MyScript
是如何实现的。如果你发现输出被缓冲了,你必须在你的实现中强制它。例如,如果是 python 程序,则使用 sys.stdout.flush();如果是 C 程序,则使用 fflush(stdout)。
刚刚在这里发现的问题是您必须等待从脚本运行的程序完成它们的工作。
如果在你的脚本中你在后台运行程序,你可以尝试更多。
一般来说,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
谢谢@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'
它奏效了!
请注意手册页中的警告。这可能对您不起作用。
替代 stdbuf 是awk '{print} END {fflush()}'
我希望有一个内置的 bash 来执行此操作。通常它不应该是必需的,但是对于旧版本,文件描述符上可能存在 bash 同步错误。
我在 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
;;
我不知道它是否会起作用,但是打电话sync
呢?
不管你喜不喜欢,这就是重定向的工作原理。
在您的情况下,您的脚本的输出(意味着您的脚本已完成)重定向到该文件。
您要做的是在脚本中添加这些重定向。