コマンドライン上でのデータ突き合わせ方法

このQ&Aのポイント
  • UNIX系のコマンドのみを利用して、二種類のファイルを突き合わせする方法がないか悩んでいます。
  • file_a.csvに記載されたIDが、file_b_01.csv~file_b_20.csvのIDにあるか確認し、一致した場合はその行を別ファイルに出力します。
  • grepを使用した場合、行全体がスキャンの対象になるため使用できません。コマンドラインからperlなども使えない状態です。何かいい方法はないでしょうか。
回答を見る
  • ベストアンサー

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

コマンドライン上でのデータの突き合わせについて 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なども使えない状態です。 何かいい方法はないでしょうか。

質問者が選んだベストアンサー

  • ベストアンサー
  • trapezium
  • ベストアンサー率62% (276/442)
回答No.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 すれば。

yasagure-kun
質問者

お礼

ありがとうございます。ようやく理解できました。 read a の「a」はリダイレクトしたfile_a.csvが対象だったんですね。 > awk -F, '{if ($3=="'${a}'") print $0}' file_b* また、awkで切り出しつつ比較して、そのまま出力という事ができるとは 思ってもいませんでした。 これで何とかなりそうです。とても助かりました。

その他の回答 (4)

回答No.4

まだよくわかりません。 > 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ごと?

yasagure-kun
質問者

お礼

返信が遅くなって申し訳ありません。 > これ逆じゃないですか? > 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`という使い方ができるとは、想像もしていませんでした。 シェルスクリプトを勉強して出直してきます。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

あと, file_b_*.csv の中身が「どのくらい複雑なのか」によっても変わってきます. つまり「カンマで区切られたデータ」の中にカンマが含まれていたりするとめんどくさい. そうじゃなくて単に「3つ目のカラムにある」というだけなら, grep でも awk でも.

回答No.2

重要な情報が提示されていないので明確な回答がつきにくいと思います file_a.csv にある「ID」はどう並んでいるんでしょうか? csvというからにはカンマ(もしくは他の文字)で区切られているのでしょうが 改行はあるのですか? 1行1IDで構成されていれば#1さんのご提案どおり簡単な話だと思います。 んじゃfile_a.csvをそういう構成に変換すればよいということです カンマを改行に置換すれば済みます 出力先となるファイルの作り方もどうしたいのかわかりません 該当する行全体を出力するのか ID毎に1つずつファイルをつくるのか 1つのファイルに追記していくのか。 行全体の出力で1つのファイルだと cat file_b_*.csv を何らかの形でソートした事と同一かも知れない 質問内容からは難しさが読み取れません 入出力データがどんな状態なのかはっきりさせると 有用・的確なアドバイスを得られるかもしれません

yasagure-kun
質問者

お礼

ご指摘ありがとうございます。 ■目的 ・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していると検討はつけたのですが・・・。 他に必要な情報がありましたら、ご指摘ください。

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.1

別に grep でも良さそうな気がする while read a; do grep -h "^${a}," file_b*.csv; done < file_a.csv awk でもいいかもしんない。

yasagure-kun
質問者

お礼

申し訳ありません。 質問が間違っていました。file_bのマッチさせたいフィールドが3つ目のフィールドとなります。 file_b*.csv aaaa,bbbb,ユニークID,cccc・・・・ awkの場合だと、該当箇所だけ切り出してチェックはできると思いますが、マッチした行の出力方法がわかりませんでした。

関連するQ&A

  • エクセルでピボットを組むと、集計されたデータの一番上にしかデータが出て

    エクセルでピボットを組むと、集計されたデータの一番上にしかデータが出てこなくなります。 集計した上で、すべての行にデータを入力することはできないのでしょうか。 【現状】 大分類  中分類  小分類 AAAAA  BBBBBB  CCCCCC (空白) (空白) CCCCCC (空白) DDDDDD  EEEEEE (空白) (空白) EEEEEE     ↓↓↓↓ 【理想】 大分類  中分類  小分類 AAAAA  BBBBBB  CCCCCC AAAAA  BBBBBB  CCCCCC AAAAA  DDDDDD  EEEEEE AAAAA  DDDDDD  EEEEEE もしやり方があるのであれば、教えてください。

  • excel VBAをやっているのですがデータ1かデータ2のどちらかが空

    excel VBAをやっているのですがデータ1かデータ2のどちらかが空欄だったら、チェックに未入力と入力するにはどのような方法でやればいいのですか? どちらかというのがうまく出来ません・・・ データA データB チェック aaaaa bbbbbb aaaaa        未入力 ccccc cccccc こんな感じにやりたいのですが・・・

  • findコマンドについて

    unix コマンドについて質問です。 ファイル1の中に"A=1" ," B=2", "C=3"という文字列が入っているとします。 ファイル2の中に"D=1" , "E=2", "F=3"という文字列が入っているとします。 そのことを自分は知りません。 Aという文字が入っているファイルを探す為、 以下のfindコマンドを打ち、ファイル1のlsの結果が表示されました。 find ./ -type f -exec grep 'A' {} \; -ls その後、出力されたファイル1の中からBとCでgrepしてB=2", "C=3"を表示したいと思います。 上のコマンドと併せて一度でできるコマンドを教えていただけないでしょうか? 宜しくお願いします。

  • 複数行にわたる処理

    こんばんは。perl初心者です。 どうしてもわからないので、どうかアドバイスよろしくお願いします。 一つのファイルに、以下のような3行で一組の塊がある場合、 <line_a>: aaaaaa <line_b>: BBBBBB <line_c>: cccccc <line_a>: aaaaaa <line_b>: bbbbbb <line_c>: cccccc ... <line_b>の行に文字列"BBBBBB"が含まれていたら、<line_a>の内容を"aaaaaa"から"AAAAAA"に置換してファイルを上書きしたいのですが、 どうやったらいいのでしょうか・・・。

    • ベストアンサー
    • Perl
  • エクセル/行選択の仕方を教えてください。

    はじめまして。 エクセルで、3行ごとに選択し、選んだ情報を列に移す作業をしたいのですが 1800行あり手で選択するのが大変です。 行いたい作業は以下の通りです。 A列 AAAAAA BBBBBB CCCCCC AAAAAA BBBBBB CCCCCC AAAAAA BBBBBB CCCCCC AAAAAA ↓ A列 B列 C列 AAAAAA BBBBBB CCCCCC AAAAAA BBBBBB CCCCCC AAAAAA BBBBBB CCCCCC 一行一行選択して列に移すのではなく選択できる方法がありましたら 是非ご教授ください。 よろしくお願いいたします。

  • エクセルのデータを一部抽出する方法をお教えください

    こんにちは。宜しくお願い致します。 エクセルのA列に以下の番号が入っています。  A  ――――――― 1|AAAAAA-01 2|AAAAAA-02 3|AAAAAA-03 4|BBBBBB-01 5|BBBBBB-02 6|BBBBBB-03 7|CCCCCC-01 8|CCCCCC-02 9|CCCCCC-03 10|・ 11|・ 12|・ 同じ番号にそれぞれ「-01」「-02」「-03」がついてしまっています。 この情報で、「-01」のみの行を抽出したいのですが、難しく考えてしまったせいか、上手く行きません。 エクセルにお詳しい方、方法を教えて下さい。 宜しくお願い致します。

  • コマンドライン引数で正規表現を指定したい

    Perlでgrep -Aのようなことがしたいです。 ---------------grep.pl------------------------------- use strict; use warnings; #コマンドライン引数の確認 if(@ARGV != 3){ die "USAGE: \0 [num] [regex] [file]"; } #数字の読み込み my $num = $ARGV[0]; #正規表現の読み込み my $regex = qr/$ARGV[1]/; #ファイル名の読み込み my $file = $ARGV[2]; #該当行を保存する配列 my @lines = (); open my $fh, '<', "$file" or die "$!"; my @file = <$fh>; close $fh; for(my $i=0; $i<@file; $i++){ if($file[$i] =~ /$regex/){ push @lines, $file[$i]; for(my $j=1; $j<=$num; $j++){ if($i+$j < @file){ if($file[$i+$j] !~ /$regex/){ push @lines, $file[$i+$j]; }else{ last; } }else{ last; } } } } foreach my $item (@lines){ print $item; } ------------------------------------------------- ところが、正規表現がうまく読み込めません。 perl grep.pl 2 \d{4}\/\d{2}\d{2} test.txt などとしても、\d{4}\/\d{2}\d{2}の部分が機能しません。 コマンドライン引数から正規表現を指定するにはどうしたらよいでしょうか。

    • ベストアンサー
    • Perl
  • Windows上にて、コマンドでuncompressしたい。

    Windows上にて、コマンドでuncompressしたい。 UNIXにて、compressしたファイルをWindows上にて、uncompressしたいのですが、 何か良い方法があればご教授願います。 ・ UNIXにて、ファイルをCompressする。(AAAAA.Z) ・ Windowsにて、AAAAA.Zをコマンドにて解凍したい。 以上、宜しくお願い致します。

  • UNIXのソートコマンドについて

    はじめまして、UNIXのsortコマンドについて質問です。 CSVファイルをソートする際、1行目がタイトル行で2行目以降をソートしたいのですがよい方法をご存知ですか? OSはSunOSでバージョンは3.8くらいだったと思います。 よろしくお願いします。

  • unixのコマンドで+を付けたい

    unixのたとえば、 grep "#E200#" file1|cut -d# -f1 とかで 43 78 23 17 とかの数字を出してきて、これを 43+78+23+17 になるように+をつけるには どんなコマンドがよろしいでしょうか? trを使ってなんかできませんでしょうか? これができたら、TOTAL=43+78+23+17でコマンド代入 にして bc scale=2 $TOTAL 161 quit したいのですが。 よろしくお教えください。(unix 超初心者です)