csv ファイルと別のテキスト ファイル (file1.csv と file2.txt など) があります。テキスト ファイルには 1 つの列があります。ここで、テキスト ファイルのデータに基づいて csv ファイルをフィルター処理したいと考えています。例えば、
file1.csv ----------- 1、a、b、c 2、d、e、f 3,g,d,g file2.txt ----------- 1 3
私は結果が欲しい-
1、a、b、c 3,g,d,g
このコマンドを試してください:
awk -F, 'FNR==NR{a[$0];next};$1 in a' file2.txt file1.csv
ロジックは単純です。
FOR each line in 'file2.txt' and 'file1.csv'
IF line is from 'file2.txt'
store it to array 'a'
CONTINUE
ENDIF
IF column 1 of line is in 'a'
PRINT line
ENDIF
ENDFOR
を使用した解決策については、 kevによる回答awk
を参照してください。
を使用した改善されたソリューションについては、プロセス置換grep -f
の使用を検討してください。bash
grep -f <(sed 's/.*/^&,/' file2.txt) file1.csv
これは、各行の先頭にキャレットを置き、最後にカンマを配置するためsed
に行で使用file2.txt
するため、 (GNU?) によって正規表現として扱われる場合grep
、パターンは行の先頭にある正確なフィールド値のみに一致します. をお持ちでない場合はbash
、次のものを使用できる場合があります。
sed 's/.*/^&,/' file2.txt | grep -f - file1.csv
ただし、grep
指定したときにすべてのバージョンの標準入力が読み取られる-f -
わけではありません (たとえば、Mac OS X のバージョンでは読み取れませんが、GNUでは読み取られますgrep
)。
join
または、適切な並べ替えでコマンドを使用することもできます。
join -o 1.1,1.2,1.3,1.4 -t, <(sort file1.csv) <(sort file2.txt)
ファイルが既にソートされていると確信している場合は、次のように単純化できます。
join -o 1.1,1.2,1.3,1.4 -t, file1.csv file2.txt
Perl では、以下を使用できます。
#!/usr/bin/env perl
use strict;
use warnings;
my $file = 0;
my %rows;
while (<>)
{
chomp;
$rows{$_}++ if ($file == 0);
if ($file == 1)
{
my($id) = split /,/;
print "$_\n" if defined $rows{$id};
}
}
continue
{
$file = 1 if eof;
}
おそらく他の方法もあるでしょう。たとえば、Text::CSVなどのモジュールの用途を見つけることができます。
ただし、このコードは各行を読み取ります。最初のファイルからのものである場合は$rows{$_}++
、番号が表示されたことを記録するエントリを作成します。順序と繰り返しは関係ありません。2 番目 (およびそれ以降) のファイルでは、最初のコンマ区切りフィールドを行から分割し、その番号が最初のファイルで見つかったかどうかを確認します。その場合は、行全体を出力します。このcontinue
ブロックは、コードが最初のファイルで (特に) EOF に到達したことを検出し、到達した$file = 1;
ときを設定します。awk
解と同形です。これは少し冗長です。-a
モード (モード)がありawk
ますが、2 つのファイルを異なる方法で処理する必要があるため、正しく機能させるのは少し難しいです。
grep -f
これらのうち、大きすぎない限り、ソリューションはおそらく最も適切であると思いますfile2.txt
(制限がどのくらいになるかはわかりませんが、おそらく驚くほど大きいでしょう)。
汎用の CSV ファイル操作ツールについては、csvfixを検討してください。
以下のコマンドを試してください。
grep -F -f file2.txt file1.csv
1,a,b,c
3,g,d,g
Windows コマンド バージョンの場合:
findstr /G:file2.txt file1.csv > result.csv