使用管道时修改文件的最佳方法?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用管道时修改文件的最佳方法?相关的知识,希望对你有一定的参考价值。
我经常有shell编程任务,我遇到这种模式:
cat file | some_script > file
这是不安全的 - 在some_script开始写入之前,cat可能没有在整个文件中读取。我真的不想将结果写入临时文件(它的速度很慢,而且我不希望增加复杂性来考虑一个独特的新名称)。
也许,有一个标准的shell命令会缓冲整个流,直到达到EOF为止?就像是:
cat file | bufferUntilEOF | script > file
想法?
你在找sponge。
在这里使用临时文件是正确的解决方案。当您使用像'>'这样的重定向时,它由shell处理,无论您的管道中有多少命令,shell都可以自由删除并在执行任何命令之前覆盖输出文件(在管道设置期间)。
像许多其他人一样,我喜欢使用临时文件。我使用shell process-id作为临时名称的一部分,这样如果脚本的多个副本同时运行,它们就不会发生冲突。最后,如果脚本成功,我只会覆盖原始文件(使用布尔运算符短路 - 它有点密集但对于简单的命令行非常好)。把它们放在一起,它看起来像:
some_script < file > smscrpt.$$ && mv smscrpt.$$ file
如果命令失败,这将保留临时文件。如果要清除错误,可以将其更改为:
some_script < file > smscrpt.$$ && mv smscrpt.$$ file || rm smscrpt.$$
顺便说一下,我摆脱了对猫的不良使用,并用输入重定向取而代之。
使用mktemp(1)
或tempfile(1)
可以省去必须考虑唯一文件名的费用。
使用临时文件比尝试缓冲管道中的数据更好。
它几乎违背了管道缓冲它们的目的。
为了回应the OP's question above关于在没有外部依赖性的情况下使用sponge
,以及在@D.Shawley's answer上构建,你可以产生仅仅依赖于gawk
的海绵效果,这在Unix或类Unix系统上并不罕见:
cat foo | gawk -voutfn=foo '{lines[NR]=$0;} END {if(NR>0){print lines[1]>outfn;} for(i=2;i<=NR;++i) print lines[i] >> outfn;}'
检查NR>0
是截断输入文件。
要在shell脚本中使用它,请将-voutfn=foo
更改为-voutfn="$1"
或shell用于filename参数的任何语法。例如:
#!/bin/bash
cat "$1" | gawk -voutfn="$1" '{lines[NR]=$0;} END {if(NR>0){print lines[1]>outfn;} for(i=2;i<=NR;++i) print lines[i] >> outfn;}'
请注意,与真正的sponge
不同,这可能仅限于RAM的大小。如有必要,sponge
实际上会在临时文件中缓冲。
我认为最好的方法是使用临时文件。但是,如果您需要其他方法,可以在应用程序开始接收输入之前使用类似awk
的内容来缓冲输入到内存中。以下脚本将把所有输入缓冲到lines
数组中,然后开始将其输出到管道中的下一个使用者。
{ lines[NR] = $0; }
END {
for (line_no=1; line_no<=NR; ++line_no) {
print lines[line_no];
}
}
如果您愿意,可以将其折叠成单行:
cat file | awk '{lines[NR]=$0;} END {for(i=1;i<=NR;++i) print lines[i];}' > file
有了这些,我仍然建议使用临时文件输出,然后用它覆盖原始文件。
以上是关于使用管道时修改文件的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章