• ベストアンサー

ファイルの結合

UNIX上で下記のようなfileA、fileBから fileA aaa,aaa,222 aaa,bbb,111 aaa,ccc,333 fileB aaa,aaa,111 aaa,bbb,222 aaa,ccc,333 aaa,ddd,999 下記のようなfileCを作ろうとしています。 fileC aaa,aaa,222 aaa,bbb,111 aaa,ccc,333 aaa,ddd,999 そこで、以下のように一列目と2列目をキーにして、sortすることにしました。 cat fileA fileB | sort -u -k1,2 -t, > fileC すると、fileCは下記のようになりました。(一行目の3列目が222ではなく、111になってしまいました) fileC aaa,aaa,111 aaa,bbb,222 aaa,ccc,333 aaa,ddd,999 キー項目以外はfileAを優先させたいのですが、なにか良い方法はないでしょうか?

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

  • ベストアンサー
  • nightowl
  • ベストアンサー率44% (490/1101)
回答No.2

こんにちは。もうご自分で解決されましたでしょうか? GNU sort 2.0.8(GNU textutils)では最初からお望みの結果が出たのですが、 処理系依存の動作のようですので、No.1 の方のアドバイスに沿って AWK の一行野郎(ワンライナー)をどうぞ。 $ awk -F, '{if (a[$2] != $2) {a[$2] = $2; print $0}}' fileA fileB 行ごとに第2フィールドを連想配列(ハッシュ)に突っ込んでいきます。 そして、現在行の第2フィールドが連想配列に登録されていない、 つまりこの値が初めて現れた行のみを出力するという考え方です。 この結果をソートする必要があれば、改めてパイプで sort に出力を食わせてください。 以下、余談です。AWK の子孫である Perl や Ruby には AWK エミュレーションモードなるものがありますので、試してみましょうか。 perl -F, -ane ' if ($a{$F[2]} ne $F[2]) {$a{$F[2]} = $F[2]; print}' fileA fileB 「-F,」は sort での「-t,」と同じです。「-an」で AWK の動作を模倣するとお考え下さい。 ちなみに「-pn」だと sed と同じような挙動になります。 分割されたフィールドは配列 @F に入り、$F[n](n は添え字)で呼び出せます。 ここではハッシュ(連想配列)は $a になります。 「$」で始まるのが Perl 変数の特徴。 また、Ruby ではこうなります(Perl とはどこが違うでしょうか?)。 Ruby は環境によってはインストールされていないかもしれません。 ruby -F, -ane 'BEGIN{$a={}}; if $a[$F[2]] != $F[2]; $a[$F[2]] = $F[2]; print end' fileA fileB うーん、Perl よりも若干長くなっていますね。 しかも BEGIN なんとかも増えてるし^^; これはハッシュオブジェクトを作り、 それをグローバル変数に入れて持ち歩く必要があるためで、 スクリプトの規模が大きくなれば可読性は逆転します。 こっちの方がまだいいかもしれません。 ruby -F, -an -e 'BEGIN{$a=Hash.new}' -e 'if $a[$F[2]] != $F[2] then $a[$F[2]] = $F[2]; print end' fileA fileB これ以上複雑になった場合はスクリプトをファイルにした方がいいですね。 (所詮エミュレーションはエミュレーションですので、 一行野郎だけで言語の美しさを判断しないでください) なお、Ruby には対話的な実行環境コマンド「irb」があります。 Python インタプリタ自体も対話環境ですし、 Perl でも裏技的ですがデバッガ起動すれば対話的に使えます。 (「perl -d /dev/null」で起動するとすぐデバッガのコマンドラインに落ちます。 継続行の行末には「\」をつけてください。この配慮は irb や python では不要です)

その他の回答 (2)

  • nightowl
  • ベストアンサー率44% (490/1101)
回答No.3

訂正です。 >ちなみに「-pn」だと sed と同じような挙動になります。 「-p」と「-n」は背反するオプションなので、「-p」だけでした。 以下はPerl や Ruby の AWK エミュレーションの際使える主なオプションです。 -a: AWK のように行をスプリットしてフィールドにする(-n や -p と併用) -e: 以下の文字列がコマンドであることを示す -n: ファイルまたは標準入力から自動的に一行ずつ読み込む(デフォルト入力)   ループを形成 -p: -n と同様だが、読み込んだ行またはそれを処理したものも   自動的に出力(デフォルト出力) 詳しくはそれぞれのコマンドを「--help」で起動してみてください。 どうも失礼しました。

b-takeda
質問者

お礼

回答ありがとうございます。又お礼が遅れて申し訳ありません。 今回の件、シェルで書くのを諦めてCで書こうとしていました。が、今までほとんど知らなかったんですが、AWKは便利そうですね。特に連想配列で、添え字に文字列を使える辺りが。まだ時間があるのでnightowlさんのやり方でやってみようと思います。 この質問で新しい世界を垣間見た感じです。ありがとうございました。

  • shige_70
  • ベストアンサー率17% (168/946)
回答No.1

sortの-uオプションは、同一キーを1行だけ選択する、というだけで、実際にどの行が選択されるかは不定です。 通常のコマンドのみで処理するのは難しそうです。 awk等でスクリプトを書かれるのがよろしいかと思います。

b-takeda
質問者

お礼

shige_70さんアドバイスありがとうございます。 質問後にいろいろ試していまして、以下のようにやってみました。 fileAの各行先頭に「0,」、fileBの各行先頭に「1,」をつけて cat fileA fileB | sort -u -k2,3 -t, > fileC を実行すると、fileCは以下のようになり、一見上手くいっているように見えます。 0,aaa,aaa,222 0,aaa,bbb,111 0,aaa,ccc,333 1,aaa,ddd,999 しかし、たまたま上手くいっているだけかな。。と思います。 内部的なところはshige_70さんのおっしゃる様に不定ですよね。 他の方法を考えてみようと思います。 ありがとうございました。

関連するQ&A