用文本文件中的行号从另一个文件中删除行号

我有一个文本文件,包含一个巨大的行号列表,我必须从另一个主文件中删除。 这是我的数据看起来像

lines.txt

1 2 4 5 22 36 400 ... 

documents.txt

 string1 string2 string3 ... 

如果我有一个简单的行号我可以很容易地使用

sed -i '1d,4d,5d' documents.txt

但是我需要删除很多行号。 另外,我可以使用bash / perl脚本将行号存储在数组中,并回显不在数组中的行。 但是我想知道是否有一个内置的命令来做到这一点。

任何帮助将不胜感激。

Solutions Collecting From Web of "用文本文件中的行号从另一个文件中删除行号"

awk oneliner应该为你工作,见下面的测试:

 kent$ head lines.txt doc.txt ==> lines.txt <== 1 3 5 7 ==> doc.txt <== a b c d e f g h kent$ awk 'NR==FNR{l[$0];next;} !(FNR in l)' lines.txt doc.txt b d f h 

正如列昂所建议的,我补充一些解释:

 awk # the awk command 'NR==FNR{l[$0];next;} # process the first file(lines.txt),save each line(the line# you want to delete) into an array "l" !(FNR in l)' #now come to the 2nd file(doc.txt), if line number not in "l",print the line out lines.txt # 1st argument, file:lines.txt docs.txt # 2nd argument, file:doc.txt 

那么,我不会说Perl和bash,在审判之后我会开始痛苦的审判。 但是,Rexx很容易做到这一点。

 lines_to_delete = "" do while lines( "lines.txt" ) lines_to_delete = lines_to_delete linein( "lines.txt" ) end n = 0 do while lines( "documents.txt" ) line = linein( "documents.txt" ) n = n + 1 if ( wordpos( n, lines_to_delete ) == 0 ) call lineout "temp_out,txt", line end 

这将使您的输出保留在temp_out.txt中,您可以根据需要将其重命名为documents.txt。

这是一个使用sed

 sed ':a;${s/\n//g;s/^/sed \o47/;s/$/d\o47 documents.txt/;b};s/$/d\;/;N;ba' lines.txt | sh 

它使用sed来构建一个sed命令并将其传递给shell来执行。 由此产生的sed命令看起来像“sed”3d; 5d; 11d“documents.txt。

为了构建它,外部sed命令添加了一个d; 在每个数字之后,循环到下一行,分支回到开头( N; ba )。 当到达最后一行( $ )时,所有的换行符都被删除, sed '被前置,最后的d' documents.txt被追加。 然后,由于没有指定标签, b分支出:aba循环。

以下是如何使用joincat -n (假设lines.txt被排序):

 join -t $'\v' -v 2 -o 2.2 lines.txt <(cat -n documents.txt | sed 's/^ *//;s/\t/\v/') 

如果lines.txt没有排序:

 join -t $'\v' -v 2 -o 2.2 <(sort lines.txt) <(cat -n documents.txt | sed '^s/ *//;s/\t/\v/') 

编辑:

修复了原始版本只输出documents.txt中每行的第一个字的join命令中的一个错误。

这可能适用于你(GNU sed):

 sed 's/.*/&d/' lines.txt | sed -i -f - documents.txt 

要么:

 sed ':a;$!{N;ba};s/\n/d;/g;s/^/sed -i '\''/;s/$/d'\'' documents.txt/' lines.txt | sh 

我在Unix SE上问了一个类似的问题,并得到了很好的答案,其中包括下面的awk脚本:

 #!/bin/bash # # filterline keeps a subset of lines of a file. # # cf. https://unix.stackexchange.com/q/209404/376 # set -eu -o pipefail if [ "$#" -ne 2 ]; then echo "Usage: filterline FILE1 FILE2" echo echo "FILE1: one integer per line indicating line number, one-based, sorted" echo "FILE2: input file to filter" exit 1 fi LIST="$1" LC_ALL=C awk ' function nextline() { if ((getline n < list) <=0) exit } BEGIN{ list = ENVIRON["LIST"] nextline() } NR == n { print nextline() }' < "$2" 

另一个C版本,性能更高一些: