【发布时间】:2018-08-06 17:35:49
【问题描述】:
我们知道 sed 命令会遍历文件的每一行,并且对于每一行,它都会遍历给定的命令列表并执行一些操作。但是当文件非常大时,重复操作的时间和资源成本可能会很可怕。
假设我有一个行号数组,我想将其用作使用 sed 命令删除或打印的地址(例如A=(20000 30000 50000 90000)),并且有一个非常大的目标文件。
最简单的方法可能是: (@John1024 备注,注意每个循环的行号变化)
( for NL in ${A[@]}; do sed "$NL d" $very_large_file; done; )>.temp_file;
cp .temp_file $very_large_file; rm .temp_file
上面代码的问题是,对于数组的每个索引行号,它需要循环整个文件。
为避免这种情况,可以:
#COMM=`echo "${A[@]}" | sed 's/\s/d;/g;s/$/d'`;
#sed -i "$COMM" $very_large_file;
#Edited: Better with direct parameter expansion:
sed -i "${A[@]/%/d;}" $very_large_file;
它首先打印数组并将其SPACE和END_OF_LINE替换为sed的d命令,使字符串看起来像"20000d;30000d;50000d;90000d";在第二行,我们将此字符串视为sed 的命令列表。结果是,使用此代码,它只循环文件一次。
此外,对于就地操作(参数-i),即使感兴趣的最大行数已经过去,也不能退出使用q 和sed,因为如果是这样,那一行之后的行(eg 90001+) 会消失(看来就地操作只是用stdout覆盖文件)。
更好的想法?
(回复@user unknown:) 我认为如果我们在所有索引行都通过后设法“退出”循环,效率会更高。由于上述原因,我们不能使用sed -i。将每一行打印到文件比复制文件花费更多时间(例如cat file1 > file2 和cp file1 file2)。我们可以从这个概念中受益,使用任何其他方法或工具。这是我所期望的。
PS:这道题的重点是“Lines location”和“Efficiency”; “删除行”操作只是一个例子。对于实际任务,还有更多 - 追加/插入/替换、字段分离、案例判断以及读取/写入文件、计算等。 换句话说,它可能会调用各种操作,创建或不创建子shell,关心变量传递,......所以,使用的工具应该允许我进行线性处理,问题是如何让自己进入感兴趣的线,做各种操作。
感谢任何cmets。
【问题讨论】:
-
您说得对,
sed -i并没有真正就地写入。它首先创建一个新文件,然后用新文件替换旧文件。有关这方面的详细信息,请参阅this answer。 -
一个关键问题是,即使您只删除文件中的一行,也必须更改后面每一行的字节位置。有没有“删除”行的替代方法?您能否在不更改行长并因此避免移动所有剩余行的情况下注释掉一个(例如,用
#替换行中的第一个字符)? -
@John1024 是的,我忘记了这个问题,谢谢。对于方法1,存在此问题,您的建议应该有效;或者只是不使用就地参数,而是引用整个代码并将输出重定向到文件。对于方法2,不存在此问题。
-
‘sed file1 > file1’有未定义的行为。我不确定循环是否修复了它。显然你的第二个解决方案更好。
-
@zzxyz 也许我应该用另一种方式说...... sed without quiet "-n" 将内容打印到标准输出,(通常到屏幕上);但是使用 ( .. ) > 文件,括号中的标准输出重定向到文件。啊,顺便说一句,有输入错误,现在已经编辑了。