5

別のメインファイルから削除する必要がある行番号の巨大なリストを含むテキストファイルがあります。これが私のデータのようです

lines.txt

1
2
4
5
22
36
400
...

documents.txt

string1
string2
string3
...

行番号の短いリストがあれば、簡単に使用できます

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

しかし、削除しなければならない行番号はたくさんあります。また、bash / perlスクリプトを使用して、行番号を配列に格納し、配列にない行をエコーすることもできます。しかし、それを実行するための組み込みコマンドがあるかどうか疑問に思いました。

どんな助けでも大歓迎です。

4

5 に答える 5

10

awkonelinerが機能するはずです。以下のテストを参照してください。

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

Levonが提案したように、私はいくつかの説明を追加します:

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
于 2012-07-06T21:16:19.147 に答える
2

ええと、私はPerlを話しませんし、試練の後に苦痛な試練を開発します。ただし、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に名前を変更できます。

于 2012-07-06T21:22:30.817 に答える
2

これを行う方法は次のsedとおりです。

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

sedコマンドを作成し、sedそれを実行するシェルにパイプするために使用します。結果のsedコマンドは、単に `sed '3d; 5d;11d'documents.txtのようになります。

それを構築するために、外部sedコマンドはd;各番号の後にアフターを追加し、次の行にループして、先頭に分岐します(N; ba)。最後の行()に達する$と、すべての改行が削除され、sed '先頭に追加され、最後dの行' documents.txtが追加されます。次に、ラベルが指定されていないため、 -ループから最後までb分岐します。:aba

joinおよびを使用してこれを行う方法は次のcat -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/')

編集:

join元のバージョンがdocuments.txtの各行の最初の単語のみを出力するコマンドのバグを修正しました。

于 2012-07-06T21:50:56.280 に答える
1

これはあなたのために働くかもしれません(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
于 2012-07-07T01:21:11.573 に答える
0

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バージョン:

于 2016-01-21T10:48:57.687 に答える