![当行号存储在文件中时,用字符串替换多行](https://linux22.com/image/86827/%E5%BD%93%E8%A1%8C%E5%8F%B7%E5%AD%98%E5%82%A8%E5%9C%A8%E6%96%87%E4%BB%B6%E4%B8%AD%E6%97%B6%EF%BC%8C%E7%94%A8%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%9B%BF%E6%8D%A2%E5%A4%9A%E8%A1%8C.png)
我有一个只有几百万行的文件,但都是一样的。仅举一个例子:
Known
Known
Known
Known
Known
Known
...
我有另一个包含几千行号的文件,例如:
3
5
6
...
我想知道是否有一种快速方法可以使用 bash 命令将这些行替换为另一个字符串,例如 UnKnown。基于我想生成的示例:
Known
Known
UnKnown
Known
UnKnown
UnKnown
...
答案1
解决方案awk
:
$ awk 'NR==FNR{a[$1]++;next}
{
if(FNR in a){
print "UnKnown"
}
else{
print
}
}' nums file
Known
Known
UnKnown
Known
UnKnown
UnKnown
解释
NR==FNR{a[$1]++;next}
:NR
是输入的当前行号和FNR
当前文件的当前行号。仅当读取第一个文件时,两者才相等。因此,该表达式会将每个行号($1
第一个文件的第一个字段 )保存为数组中的键a
,然后跳到下一行。if(FNR in a){ print "UnKnown"}
:如果当前文件的行号在第一个文件中,则打印“UnKnown”。else {print}
:如果没有,则打印当前行。
答案2
这是吉尔斯对“如果要更改的行数很少”场景的答案的变体。它不是构建内联 sed 表达式,而是创建一个 sed 脚本,通过 stdout/stdin 管道发送到 sed 以使用 -f - 进行读取。这样做可以避免命令行长度限制的任何问题。或者,您可以将 sed 脚本保存到“临时”文件,然后将 sed 指向该文件。
我引入的另一个变体是 sed 的“c”命令,它表示用给定的文本替换选定的行。 “c”命令的语法有点不寻常,因为它需要一个反斜杠、换行符,然后是新文本。
sed 's/$/c\\\nNew String/' line-number-file | sed -f - input-file > output-file
第一个 sed 命令创建一个中间 sed 脚本作为第二个 sed 的输入,方法是用“c、反斜杠、换行符、新字符串”序列“替换”行尾 ( $
):
3c\
New String
5c\
New String
6c\
New String
要更改用作替换的文本,请进入第一个 sed 部分,并将“New String”替换为您想要的任何内容。
如果你想替换原始输入文件中的文本,并且你的 sed 支持该-i
标志,那么你可以将命令更改为:
sed 's/$/c\\\nNew String/' line-number-file | sed -f - -i input-file
答案3
一种可能性是通过 awk 过滤行。如果要更改的行列表很小,请将其传递给命令行上的 awk。
awk <original.txt >modified.txt -v lines="$(cat lines-to-change.txt)" '
BEGIN {split(lines, a); for (i in a) change[a[i]]=1}
NR in change {$0 = "Un" $0} # or $0 = "UnKnown"
1
'
如果要更改的行数非常少并且要修改的文件非常大,则 sed 可能会更快。使用 sed,您需要构建一个包含要应用于每一行的替换的脚本。
sed "$(<lines-to-change.txt sed 's/$/s:^:Un:/')" <original.txt >modified.txt
如果需要更改很大一部分行,则前两种方法将遇到命令行长度限制。这是 awk 的一种修改方法,它并行读取两个文件。如果lines-to-change.txt
已经排序,则可以使用getline n <"lines-to-change.txt"
代替"sort -n lines-to-change.txt" | getline n
。
awk <original.txt >modified.txt '
BEGIN {"sort -n lines-to-change.txt" | getline n}
NR==n {$0 = "Un" $0; n = 0; "sort -n lines-to-change.txt" | getline n}
1
'