LiK*_*Kao 17 bash concurrency scripting
我目前正在尝试使用脚本将其他已启动命令的输出正确写入日志文件.该脚本将使用echo将其自己的消息写入日志文件,并且有一种方法可以管理来自其他程序的行.
主要的问题是,生成输出的程序是在后台启动的,所以我执行读取的函数可以写入日志文件.这可能是个问题吗?Echo总是只写一行,所以不应该很难确保原子性.但是我已经查看了谷歌,我发现没有办法确保它实际上是原子的.
这是当前的脚本:
LOG_FILE=/path/to/logfile
write_log() {
echo "$(date +%Y%m%d%H%M%S);$1" >> ${LOG_FILE}
}
write_output() {
while read data; do
write_log "Message from SUB process: [ $data ]"
done
}
write_log "Script started"
# do some stuff
call_complicated_program 2>&1 | write_output &
SUB_PID=$!
#do some more stuff
write_log "Script exiting"
wait $SUB_PID
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,脚本既可以自己编写,也可以编写重定向输出.这会导致文件中的havok吗?
Bri*_*ell 29
echo
只是一个简单的包装器write
(这是一个简化;请参阅下面的编辑以了解血淋淋的细节),因此要确定echo是否是原子的,查找写入是有用的.从单个UNIX规范:
原子/非原子:如果在一次操作中写入的全部量与来自任何其他进程的数据不交错,则写入是原子的.当有多个写入器将数据发送到单个读取器时,这很有用.应用程序需要知道可以预期以原子方式执行写入请求的大小.此最大值称为{PIPE_BUF}.IEEE Std 1003.1-2001的这个体积并未说明超过{PIPE_BUF}字节的写请求是否是原子的,但要求{PIPE_BUF}或更少字节的写入必须是原子的.
您可以PIPE_BUF
使用简单的C程序检查系统.如果你只打印一行输出,这不是很长,它应该是原子的.
这是一个检查以下值的简单程序PIPE_BUF
:
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("%d\n", PIPE_BUF);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在Mac OS X,这给了我512(在最小允许值的PIPE_BUF
).在Linux上,我得到4096.所以如果你的行很长,请确保你在相关系统上检查它.
编辑添加:我决定检查Bash中的实现echo
,以确认它将以原子方式打印.事实证明,echo
使用putchar
或printf
取决于您是否使用该-e
选项.这些是缓冲的stdio操作,这意味着它们填满了一个缓冲区,并且实际上只在达到换行符时(在行缓冲模式下),缓冲区被填充(在块缓冲模式下),或者你明确刷新输出用fflush
.默认情况下,如果流是交互式终端,则流将处于行缓冲模式;如果是任何其他文件,则流将处于缓冲模式.Bash从不设置缓冲类型,因此对于您的日志文件,它应默认为阻止缓冲模式.在内置结束时echo
,Bash 调用fflush
刷新输出流.因此,输出将始终在结束时刷新echo
,但如果它不适合缓冲区,则可以提前刷新.
所用缓冲区的大小BUFSIZ
可能不同,但可能不同; BUFSIZ
如果使用显式设置缓冲区setbuf
,则是默认大小,但是没有可移植的方法来确定缓冲区的实际大小.对于什么BUFSIZ
是没有便携式指南,但是当我在Mac OS X和Linux上测试时,它的大小是它的两倍PIPE_BUF
.
这是什么意思呢?由于输出echo
都是缓冲的,因此write
在填充或fflush
调用缓冲区之前,它实际上不会调用.此时,应该写出输出,并且我应该应用上面提到的原子性保证.如果stdout缓冲区大小大于PIPE_BUF
,PIPE_BUF
则将是可以写出的最小原子单位.如果PIPE_BUF
大于stdout缓冲区大小,则当缓冲区填满时,流将写入缓冲区.
因此,echo
只保证原子地写入比PIPE_BUF
stdout缓冲区的较小和最小的序列短的序列,这很可能BUFSIZ
.在大多数系统上,BUFSIZ
它更大PIPE_BUF
.
tl; dr:echo
将原子输出线,只要这些线足够短.在现代系统上,您最多可以安全地使用512字节,但无法轻松确定限制.