含sed的脚本执行时输出重定向异常,终端与日志内容不一致求助
问题背景
文件结构:
- script.sh - directory (空目录)
script.sh代码:
#!/bin/bash echo "Start" find . -type f | xargs sed -r -i '' "s/a/b/g" echo 'After find/sed'
在directory目录中执行命令:../script.sh | tee out.log,得到以下结果:
- 终端输出:
Start After find/sed
cat out.log输出:
Stbrt
提出两个问题:
- 为何终端输出与
out.log内容不同? - 脚本如何影响
out.log的内容?它不是应该在脚本执行完成后才创建吗?(可见已被修改:Start变为Stbrt)
问题解答
1. 终端输出与out.log内容不同的原因
执行../script.sh | tee out.log时,tee会同时将脚本输出写入终端和out.log,但流程中存在文件替换的干扰:
- 首先
echo "Start"的输出被tee写入终端和out.log,此时out.log内容为Start\n。 - 接着脚本执行
find . -type f,会遍历当前的directory目录,此时out.log已经被tee创建,会被find捕获并传给xargs sed。 - 你的环境是BSD sed(比如macOS默认),
sed -i ''会创建临时文件,把替换后的内容写入临时文件,再删除原out.log,将临时文件重命名为out.log。这时候tee之前打开的是旧的out.log文件句柄,后续echo 'After find/sed'的输出会写入那个已经被删除的旧文件(内核会保留它直到tee结束,但用户看不到)。 - 终端是实时输出内容,所有信息都会直接显示;但你最终
cat out.log看到的是sed替换后的新文件,里面只有被改成Stbrt的第一行内容,第二行输出根本没写入到这个新文件里。
2. 脚本如何影响out.log,以及文件创建时机
tee命令在管道启动的瞬间就会创建out.log,不是等脚本执行完才生成。当你运行command | tee file时,tee会先打开目标文件(不存在则创建),然后才开始接收管道内容。
脚本里的find命令会扫描当前目录下的所有普通文件,这时候out.log已经存在,所以会被纳入处理范围。随后sed的替换规则s/a/b/g会把文件里的a换成b,Start就变成了Stbrt。再加上前面说的sed -i替换文件的机制,导致后续的输出无法写入最终的out.log,最终文件里只剩修改后的第一行。
内容的提问来源于stack exchange,提问作者mouse_00
相关产品推荐
相关产品推荐

