当行号存储在文件中时,用字符串替换多行

当行号存储在文件中时,用字符串替换多行

我有一个只有几百万行的文件,但都是一样的。仅举一个例子:

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
'

相关内容