读写文件:tee命令

众所周知,这样的命令:

cat filename | some_sed_command >filename 

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

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

 cat file | some_sed_command | tee file >/dev/null 

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

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

 cat file | some_sed_command | tee file >/dev/null 

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

这一切都取决于首先处理哪个命令,与人们可能期望的相反, 管道中的命令不是从左到右处理的 。 不能保证首先选择哪个命令,所以人们可能会认为它是随机选择的,并且从不依赖shell而不是选择违规的命令。

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

script.sh

 #!/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 
 % bash script.sh 93 Not truncated 7 Truncated % bash script.sh 98 Not truncated 2 Truncated % bash script.sh 100 Not truncated 

所以永远不要使用像cat file | some_sed_command | tee file >/dev/null这样的东西 cat file | some_sed_command | tee file >/dev/null cat file | some_sed_command | tee file >/dev/null 。 Oli建议使用sponge

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

 $ cat file foo bar $ for ((i=0; i<100; i++)); do <<<"$(file; done $ cat file foo baz 

对于sed ,您可以使用其-i in-place参数。 它只是保存回它打开的文件,例如:

 sed -i 's/ /-/g' filename 

如果你想做一些更强大的事情,假设你做的不仅仅是sed ,是的,你可以使用sponge (来自moreutils包)来缓冲整个事情,这将在moreutils出文件之前“吸收”所有stdin。 它就像tee但function较少。 但是对于基本用法,它几乎是一个简单的替代品:

 cat file | some_sed_command | sponge file >/dev/null 

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

您可以在Ex模式下使用Vim:

 ex -sc '%!some_sed_command' -cx filename 
  1. %选择所有行

  2. ! 运行命令

  3. x保存并退出