UNIXシェルで多くのgrep awkソートを使用して、中サイズ(約10M〜100M行)のタブ区切りの列テキストファイルを処理します。この点で、UNIXシェルは私のスプレッドシートです。
しかし、私には1つの大きな問題があります。それは、IDのリストを指定してレコードを選択することです。
形式table.csv
のid\tfoo\tbar...
ファイルとIDのリストを含むids.csv
ファイルがある場合、table.csv
から存在するIDを含むレコードのみをids.csv
から選択します。
一種の https://stackoverflow.com/questions/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids が、シェルでは、 Perlではありません。
grep -F
は、IDが可変幅の場合、明らかに誤検知を生成します。 join
は、私が理解できなかったユーティリティです。まず、アルファベット順の並べ替えが必要です(通常、ファイルは数値順に並べ替えられます)が、それでも、正しくない順序について文句を言わず、一部のレコードをスキップしないと、ファイルを機能させることができません。だから私はそれが好きではありません。 IDの数が多い場合、^id\t
- sのファイルに対するgrep -fは非常に遅くなります。 awk
は扱いにくいです。
これに対する良い解決策はありますか?タブ区切りファイル用の特定のツールはありますか?追加機能も大歓迎です。
UPD:修正済みsort
-> join
grep -f
ではなくgrep -F
を意味していたと思いますが、実際にはと-w
の両方の組み合わせが必要です。
grep -Fwf ids.csv table.csv
誤検知が発生した理由は(おそらく、説明しなかったと思います)、IDが別のIDに含まれている可能性がある場合は、両方が出力されます。 -w
はこの問題を取り除き、-F
はパターンが正規表現ではなく文字列として扱われるようにします。 man grep
から:
-F, --fixed-strings
Interpret PATTERN as a list of fixed strings, separated by
newlines, any of which is to be matched. (-F is specified by
POSIX.)
-w, --Word-regexp
Select only those lines containing matches that form whole
words. The test is that the matching substring must either be
at the beginning of the line, or preceded by a non-Word
constituent character. Similarly, it must be either at the end
of the line or followed by a non-Word constituent character.
Word-constituent characters are letters, digits, and the
underscore.
-f FILE, --file=FILE
Obtain patterns from FILE, one per line. The empty file
contains zero patterns, and therefore matches nothing. (-f is
specified by POSIX.)
IDが非IDフィールドに存在する可能性があるために誤検知が発生した場合は、代わりにファイルをループします。
while read pat; do grep -w "^$pat" table.csv; done < ids.csv
または、より高速:
xargs -I {} grep "^{}" table.csv < ids.csv
個人的に、私はPerl
でこれを行います:
Perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}}
print $_ if defined($k{$F[0]}); ' table.csv
join
ユーティリティが必要です。入力ファイルを字句的にソートする必要があります。
シェルがbashまたはkshであると想定します。
join -t $'\t' <(sort ids.csv) <(sort table.csv)
ソートする必要がない場合、通常のawkソリューションは
awk -F '\t' 'NR==FNR {id[$1]; next} $1 in id' ids.csv table.csv
this SO question への回答は、結合で微笑みを回避するのに役立ちました。基本的に、結合に送信する準備としてファイルを並べ替えるときは、結合している列に基づいて並べ替えていることを確認してください。それが最初の列である場合は、ファイル内の区切り文字が何であるか、最初のフィールド(および最初のフィールド)。それ以外の場合、最初のフィールドの幅が可変である場合(たとえば)、セパレーターや他のフィールドがソート順に影響を与える可能性があります。
したがって、sortの-tオプションを使用して区切り文字を指定し、-kオプションを使用してフィールドを指定します(同じフィールドであっても、開始フィールドと終了フィールドが必要であることを忘れないでください)。そうしないと、その文字からソートされます行末まで)。
したがって、この質問のようにタブで区切られたファイルの場合、以下が機能するはずです(構造の glennの回答 のおかげで):
join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv
(参考までに、-dフラグは辞書ソートを意味します。-bフラグを使用して先頭の空白を無視することもできます。_man sort
_および_man join
_を参照してください)。
より一般的な例として、2つのコンマ区切りファイルを結合するとします。3番目の列は_input1.csv
_で、4番目の列は_input2.csv
_です。あなたは使うことができます
join -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv
ここで、_-1
_および_-2
_オプションは、最初の入力ファイルと2番目の入力ファイルで結合するフィールドをそれぞれ指定します。
Rubyを使用して同様のことを行うこともできます:
Ruby -pe 'File.open("id.csv").each { |i| puts i if i =~ /\$\_/ }' table.csv