我有几个编辑模板文本文件的脚本,通过删除“标签”并将其替换为数字等。为此,我使用
sed -i
命令。但是,我在执行脚本的服务器上遇到写入/读取时间问题,导致脚本需要很长时间才能运行,因为 sed -i 命令会在每次执行时将临时文件写入磁盘。
我是否可以使用另一种方法,即每次替换时都不会将临时文件写入磁盘?文本文件是否可以在内存中编辑,并且只有在执行所有替换后才写入,或者我可以将多个替换堆叠到同一个 sed 命令中吗?
为了澄清,脚本的形式如下:
input=shiftLeft.txt
while IFS= read -r line
do
sed -i "s/install, element = $line, at=/install, element = $line, at= -0.001 +/g" processedFiles/layoutDB.seq
done < "$input"
也就是说,我从一个文本文件中读取值,然后根据这些值在另一个文本文件中进行一些更改。对于大量值重复执行此操作。
答案1
您的问题不在于sed -i
创建许多临时文件,而是您使用同一输入文件多次运行它,并且每个文件都会为输出创建一个临时文件,如下strace
所示:
execve("/bin/sed", ["sed", "-i", "-e", "", "/tmp/foo"], 0x7fff10da5288 /* 36 vars */) = 0
openat(AT_FDCWD, "/tmp/foo", O_RDONLY) = 3
openat(AT_FDCWD, "/tmp/sedVdjaBk", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
rename("/tmp/sedVdjaBk", "/tmp/foo") = 0
+++ exited with 0 +++
解决方案是只运行sed -i
一次。
为此,首先编写一个sed
将输入文件转换为sed
程序的命令。那看起来像:
sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!"
(如果输入文件包含正则表达式有效字符,例如,我们可以改进这一点s/install, element = &, at=/\& -0.001 +/g
,但这超出了这个问题的范围)。
对此进行测试以确保您对生成的脚本感到满意。
然后我们需要另一个sed
使用转换后的文本作为其程序文件。我们可以通过告诉它从标准输入读取程序来做到这一点(尽管还有其他选择):
sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!' \
shiftLeft.txt |
sed -f - -i processedFiles/layoutDB.seq
再次测试这个(不带-i
标志),直到您满意它达到您想要的效果为止。
¹ 因为我们正在使用巴什,我们可以使用过程替换:
sed -f <(sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!' shiftLeft.txt) \
-i processedFiles/layoutDB.seq
在标准 shell 中,我们需要将转换后的文本捕获为字符串,并将其作为命令行脚本提供:
sed -e "$(sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!' shiftLeft.txt)" \
-i processedFiles/layoutDB.seq
答案2
不要在 shell 循环中重复调用 sed,只需调用 awk 一次,例如(未测试,因为您没有提供任何示例输入/输出来测试)使用 GNU awk 进行“就地”编辑和 match() 的第三个参数:
awk -i inplace '
NR==FNR { lines[$0] }
(FNR>NR) && match($0,/(.*install, element = )([^,]+)(, at=)/,a) && (a[2] in lines) {
$0 = a[0] " -0.001 +"
}
{ print }
' shiftLeft.txt processedFiles/layoutDB.seq
根据您的输入/输出的情况,可能有更好的方法来做到这一点。