读写文件:tee 命令

Ver*_*der 11 command-line bash tee

众所周知,像这样的命令:

cat filename | some_sed_command >filename
Run Code Online (Sandbox Code Playgroud)

擦除文件文件名,因为在命令之前执行的输出重定向会导致文件名被截断。

可以通过以下方式解决问题:

cat file | some_sed_command | tee file >/dev/null
Run Code Online (Sandbox Code Playgroud)

但我不确定这在任何情况下都有效:如果文件(和 sed 命令的结果)非常大,会发生什么?操作系统如何避免覆盖一些尚未读取的内容?我看到还有一个海绵命令在任何情况下都应该起作用:它比 tee 更“安全”吗?

kos*_*kos 12

可以通过以下方式解决问题:

cat file | some_sed_command | tee file >/dev/null
Run Code Online (Sandbox Code Playgroud)

没有

机会file将被截断 drop,但不能保证cat file | some_sed_command | tee file >/dev/null不会 truncate file

这完全取决于首先处理哪个命令,而不是人们所期望的,管道中的命令不是从左到右处理的。无法保证首先选择哪个命令,因此不妨将其视为随机选择,而不要依赖 shell 不选择有问题的命令。

由于在三个命令之间首先选择违规命令的机会低于在两个命令之间首先选择违规命令的机会,因此file被截断的可能性较小,但它仍然会发生

script.sh

cat file | some_sed_command | tee file >/dev/null
Run Code Online (Sandbox Code Playgroud)
#!/bin/bash
for ((i=0; i<100; i++)); do
    cat >file <<-EOF
    foo
    bar
    EOF
    cat file |
        sed 's/bar/baz/' |
        tee file >/dev/null
    [ -s file ] &&
        echo 'Not truncated' ||
        echo 'Truncated'
done |
    sort |
    uniq -c
rm file
Run Code Online (Sandbox Code Playgroud)

所以永远不要使用类似cat file | some_sed_command | tee file >/dev/null. sponge按照 Oli 的建议使用。

作为替代方案,对于大环境和/或相对较小的文件,可以使用 here 字符串和命令替换在运行任何命令之前读取文件:

% bash script.sh
 93 Not truncated
  7 Truncated
% bash script.sh
 98 Not truncated
  2 Truncated
% bash script.sh
100 Not truncated
Run Code Online (Sandbox Code Playgroud)


Oli*_*Oli 9

对于sed具体情况,你可以使用它的-i就地说法。它只是保存回它打开的文件,例如:

sed -i 's/ /-/g' filename
Run Code Online (Sandbox Code Playgroud)

如果你想做一些更强大的事情,假设你做的不仅仅是sed,是的,你可以用sponge(从moreutils包中)缓冲整个事情,这将在写出文件之前“吸收”所有标准输入。它就像tee但功能较少。不过,对于基本用法,它几乎是一种替代品:

cat file | some_sed_command | sponge file >/dev/null
Run Code Online (Sandbox Code Playgroud)

那更安全吗?确实。它可能有限制,因此如果您正在做一些巨大的事情(并且无法使用 sed 进行就地编辑),您可能希望对第二个文件进行编辑,然后mv将该文件恢复为原始文件名。这应该是原子的(因此,如果它们需要持续访问,则依赖于这些文件的任何内容都不会中断)。