電子書籍の厳選無料作品が豊富!

コマンドライン上でのデータの突き合わせについて

UNIX系のコマンドのみを利用して、二種類のファイルを突き合わせする方法がないか悩んでいます。

file_a.csv → ユニークなIDのみ
file_b_01.csv~file_b_20.csv → ユニークID,aaaaa,bbbbbb,cccccc・・・・

file_a.csvに記載されたIDが、file_b_01.csv~file_b_20.csvのIDにあるか確認し、一致した場合はその行を別ファイルに出力します。

grepを使用した場合、行全体がスキャンの対象になるため使用できません。
コマンドラインからperlなども使えない状態です。

何かいい方法はないでしょうか。

A 回答 (5件)

> 質問が間違っていました。

file_bのマッチさせたいフィールドが3つ目のフィールドとなります。

それでもほとんど一緒ですよ。

while read a; do egrep "^(.*,){2}${a}," file_b*; done < file_a.csv > out.csv

とか、${a} が正規表現に引っ掛りそうなら

while read a; do awk -F, '{if ($3=="'${a}'") print $0}' file_b*; done < file_a.csv > out.csv

あと必要なら sort | uniq すれば。
    • good
    • 0
この回答へのお礼

ありがとうございます。ようやく理解できました。

read a の「a」はリダイレクトしたfile_a.csvが対象だったんですね。


> awk -F, '{if ($3=="'${a}'") print $0}' file_b*

また、awkで切り出しつつ比較して、そのまま出力という事ができるとは
思ってもいませんでした。

これで何とかなりそうです。とても助かりました。

お礼日時:2010/06/19 15:11

まだよくわかりません。



> awkでfile_b*.csvの該当箇所を切り出し、
> file_a.csvを読み込んで比較させて、

これ逆じゃないですか?

file_a.csvが改行区切りになっているなら「切り出す」必要はなくシェルが1行ずつ読み込みます
そして検索されるファイルを1行毎に処理するgrepやawkの標準的な動作で充分です

#1さんのwhileを書き直してみました

for a in `cat file_a.csv`; do grep -h "${a}" file_b*.csv; done

whileループをforループに置き換えただけです
これならわかりますか?
(検索語が行頭ではないとのことなので「^」を外しています)


また、file_b*.csvの第3フィールドにIDがあるとのことですが
それがどれほど重要なのかが不明です。
第3ではない他のフィールドに別のIDが含まれるデータが存在すると言うのでなければ
まったく考慮する必要のない情報ということになります
grepは1行毎にその検索語があるかないかだけ見ます
もしID(と同じ文字列)を含むデータが他のフィールドに存在する可能性があるなら、
file_b*.csvの第3フィールドに限定して検索する必要があるので
awkで「切り出し」て比較することになり、ちょっと複雑な処理です。

I 台本をmickyに覚えてもらい1つずつ教えてもらう
II mickyのセリフの書かれたTシャツをタンスからさがしてminnieに持たす
    (胸だろうが背中だろうが区別しない(できない))
III minnieの持っているTシャツの胸の言葉をdonaldに覚えてもらう
IV mickyとdonaldが同じ事を言っていたら
    minnieのTシャツを箱にしまう
    そうじゃなければそのTシャツは放り投げる
V Iに戻ってmickyに次のセリフを聞く


さらに、件の「ユニークID」はfile_b*.csv群のなかに1回だけ現れるのか複数回登場するのか
はたまた存在しないこともあるのかが重要だと思います。
この場合minnieがTシャツを一度に2枚持つことになりさらに複雑化する

#何をもって「ユニーク」と呼称したのかにもよりますが
#他フィールドに含まれていたり複数回登場したりだったら「ユニーク」ではないような。



検索でヒットした行全体をそのまま出力で
元はどのファイルにあったデータなのか(file_b3.cvsだったのかfile_b14.csvなのか両方なのか)が
必要ない情報なら至極単純なgrepで事足りるのです
minnieに持たせずそのまましまうだけ。donaldも出る幕無し。

# …で出力ファイルは1つ?各IDごと?
    • good
    • 0
この回答へのお礼

返信が遅くなって申し訳ありません。

> これ逆じゃないですか?

> file_a.csvが改行区切りになっているなら「切り出す」必要はなくシェルが1行ずつ読み込みます
> そして検索されるファイルを1行毎に処理するgrepやawkの標準的な動作で充分です

切り出すつもりだったのは、file_b*.csvの方でした。

perlで書くとこんな感じのことをやりたかったのです。

# file_b*.csvをカンマで分割
# 第3フィールドがあらかじめ読み込んだ検索キーと一致したらファイル出力
my @data = split(/,/, $_);
if($data[3] eq $keys){
 print OUT $_;
}


前後してしまいますが、対象のフィールド以外に、検索キーを含む文字列が存在する可能性が
あったため、対象部分だけを切り出そうと考えました。

あらためて整理しなおしてみました。

・file_b*.csvは商品名、商品ID、商品概要などを記載した商品リストである
・file_a.csvはある条件に一致した商品IDのみを記載した改行区切りのリストである
・全ファイルを通して、第3フィールドには対象の文字列は一度しか出現しない
・商品概要の文中に検索キーである、対象の文字列が出現する可能性がある
・検索キーが第3フィールドに一致したらfile_b*.csvの行を、そのままout.csvに出力する

おっしゃるとおり、対象の文字列が複数でてくるならユニークとはいえませんが、商品IDとして
第3フィールドに出現するのはユニークなので、ユニークであると表現させていただきました。


> for a in `cat file_a.csv`; do grep -h "${a}" file_b*.csv; done

> whileループをforループに置き換えただけです
> これならわかりますか?
> (検索語が行頭ではないとのことなので「^」を外しています)

よくわかりました。
a in に対して`cat file_a.csv`という使い方ができるとは、想像もしていませんでした。

シェルスクリプトを勉強して出直してきます。

お礼日時:2010/06/19 15:00

あと, file_b_*.csv の中身が「どのくらい複雑なのか」によっても変わってきます. つまり「カンマで区切られたデータ」の中にカンマが含まれていたりするとめんどくさい.


そうじゃなくて単に「3つ目のカラムにある」というだけなら, grep でも awk でも.
    • good
    • 0

重要な情報が提示されていないので明確な回答がつきにくいと思います



file_a.csv にある「ID」はどう並んでいるんでしょうか?

csvというからにはカンマ(もしくは他の文字)で区切られているのでしょうが
改行はあるのですか?

1行1IDで構成されていれば#1さんのご提案どおり簡単な話だと思います。

んじゃfile_a.csvをそういう構成に変換すればよいということです
カンマを改行に置換すれば済みます


出力先となるファイルの作り方もどうしたいのかわかりません
該当する行全体を出力するのか
ID毎に1つずつファイルをつくるのか
1つのファイルに追記していくのか。

行全体の出力で1つのファイルだと cat file_b_*.csv を何らかの形でソートした事と同一かも知れない



質問内容からは難しさが読み取れません
入出力データがどんな状態なのかはっきりさせると
有用・的確なアドバイスを得られるかもしれません
    • good
    • 0
この回答へのお礼

ご指摘ありがとうございます。

■目的
・file_a.csvに改行区切りで並んでいる文字列を検索ワードとして、file_b*.csvの特定部分を比較する
・一致したfile_b*.csvの行を別ファイルにコピーする

当初考えた手順が
awkでfile_b*.csvの該当箇所を切り出し、
file_a.csvを読み込んで比較させて、
ヒットした行をfile_b*.csvの元のフォーマットのまま同一ファイルに書き出す、
というものでした。

ワード検索ということで真っ先にgrepを思いつきましたが、grepのヘルプを見ても、検索ワードを
別ファイルから読み込む様なオプションが見当りませんでした。

また、awkで切り出して比較するとしても、元のfile_b*.csvのフォーマットで吐き出す方法が
わからなかったのです。

awk -F , '{print $3}' file_b_01.csv | grep ファイル読み込み?


> んじゃfile_a.csvをそういう構成に変換すればよいということです
> カンマを改行に置換すれば済みます

ファイルの読み込みってそんなに簡単なんでしょうか?
試しに『grep < file_a.csv』とやってみたら怒られましたし。


#1さんの回答を考えてみたのですが、全く内容が理解できませんでした。
while read a;
  do
   grep -h "^${a}," file_b*.csv;
  done < file_a.csv

^${a}なので、file_b*csvの行頭にマッチするものをgrepしていると検討はつけたのですが・・・。

他に必要な情報がありましたら、ご指摘ください。

お礼日時:2010/06/17 10:59

別に grep でも良さそうな気がする



while read a; do grep -h "^${a}," file_b*.csv; done < file_a.csv

awk でもいいかもしんない。
    • good
    • 0
この回答へのお礼

申し訳ありません。
質問が間違っていました。file_bのマッチさせたいフィールドが3つ目のフィールドとなります。

file_b*.csv
aaaa,bbbb,ユニークID,cccc・・・・

awkの場合だと、該当箇所だけ切り出してチェックはできると思いますが、マッチした行の出力方法がわかりませんでした。

お礼日時:2010/06/16 19:58

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!