• ベストアンサー

複数ファイルを1つにするシェルスクリプト

file.1 file.2 ..... file.xx とxxが連番になっている複数のテキストファイルがあります。 これを1つのファイル"file.all"にするために以下のシェルスクリプトを作ってみました。 (公開するのも恥ずかしいのですが...^ ^;) #!/bin/sh i=1 while [ $i -le 99 ]; do  cat file.$i >> file.all  let i=i+1 done これだと99までのファイルしか指定できません。 99999...とループ回数を増やせばいいのですが、 それよりももっとスマートにやる方法があるのではと思い質問を投稿いたしました。 シェルスクリプトではなくコマンドによる方法でも結構です。 (RH7.3を使っています) よろしくお願いします。

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

  • ベストアンサー
  • me_no_car
  • ベストアンサー率24% (22/90)
回答No.9

>file.20 file.19 file.18....file.1 といった感じで結合しなくてはいけませんでした。 >このfile.xxの最大値は決まっていないのでどうにかして取得する必要がありますが 最大値取るの結構面倒ですね。。。 取る方法としては一度ループして数えるってのが一番簡単かな。。 あとはawkとsed使えばできると思います。 ここは発想の転換でこんな感じでどうでしょう? 読込むファイル先頭に追加していけば file.1,file.2・・とやっても結果はfile.20 file.19 file.18....file.1 と同じになるので最大値を取得する必要がありません。 #!/bin/sh iCnt=1 iLoop=1 LoopCnt=`ls -l file.[0-9]* | wc -l` while [ $iLoop -le $LoopCnt ] do   if [ -f file.$iCnt ]   then    cat -s file.$iCnt file.all > file.work    mv file.work file.all    let iLoop=iLoop+1   fi   let iCnt=iCnt+1 done

noname#41382
質問者

お礼

何度もご回答ありがとうございます! >ここは発想の転換でこんな感じでどうでしょう? >読込むファイル先頭に追加していけば > きましたね!こういった発想のできる頭が欲しいです! あとそれを実行できるテクニックが... >cat -s file.$iCnt file.all > file.work >mv file.work file.all > アイデアが出ても、これが思いつかないんです...^ ^;; 勉強になります!

その他の回答 (9)

  • terra5
  • ベストアンサー率34% (574/1662)
回答No.10

#8 訂正 わざわざsort -r使わなくても ls -r でできましたね(^^; ついでに、3桁まで対応させます。 この一行を置き換えてください。 filenames="`ls -r f.[0-9][0-9][0-9]` `ls -r f.[0-9][0-9]` `ls -r f.[0-9]`" ファイルのワイルドカードでの表現がわっていれば、 簡単に増やせます。 [0-9]の部分が0から9の範囲での任意の一文字という意味になってます。 [0-9][0-9]とかけば、それが二つ分。

noname#41382
質問者

お礼

補足ありがとうございました。 例と説明もありがとうございました。 >わざわざsort -r使わなくても ls -r でできましたね(^^; > すばらしいです!かなり見やすくなりました。 今回はme_no_carさんのスクリプトを使わせて頂きましたが、 terra5さんの考え方も今後必ず役に立つと思っております。 少なくとも'[0-9]'のマッチングの使い方がわかりやすく理解できなので、かなりの収穫です! ありがとうございました!

  • terra5
  • ベストアンサー率34% (574/1662)
回答No.8

#4の補足 あれだけわかれば後は大丈夫かと手抜きしたんですが(^^; 例えば, `ls f.[0-9]` `ls f.[0-9][0-9]` とすれば、最初ので f.0 ~ f.9 次の出 f.00~f.99がマッチしますのでうまくいきます。 前の回答はsort入れてましたが、lsの出力はソート済みなので不要ですね。 逆順だとソートが必要で、例えば0~99なら cat `ls f.[0-9][0-9] | sort -r` `ls f.[0-9] | sort -r` >f.all こんな感じで。 でも、10000もあると一回ではコマンドラインの制限越えそうなのでこのままでは多分無理ですね。 一行ではかきづらくなったのでスクリプトにすると、 filenames="`ls f.[0-9][0-9] | sort -r` `ls f.[0-9] | sort -r`" rm -f f.all for fname in $filenames do cat $fname >> f.all done ではどうですか。 最初に存在するファイルのリストを変数filenamesに設定し、for でリストから一つづづ名前を取り出しています。

noname#41382
質問者

お礼

再度補足のアドバイスありがとうございます! >あれだけわかれば後は大丈夫かと手抜きしたんですが(^^; > すみません...基本が全然なので、応用が利かないんです^ ^;; 基本勉強の大切さを痛感いたしました。 > (snip) >ではどうですか。 もうばっちりでした! 後は10桁ほどの数字にも対応できるようにアイデアを考えるだけですが、 またそこで自分の頭の固さが...うぅぅ 今回はみなさんの良きアドバイスのおかげで かなりアイデアの幅が広がったような気がします! ありがとうございました!

  • PAPA0427
  • ベストアンサー率22% (559/2488)
回答No.7

ごめんなさい。 >iLoop=LoopCnt1 >この部分は >iLoop=$LoopCntですか? 仰る通りです。タイプミスです。

  • PAPA0427
  • ベストアンサー率22% (559/2488)
回答No.6

#5のme_mo_carさんのモデファイすれば、OKですよ。 #!/bin/sh iCnt=1 LoopCnt=`ls -l file.[0-9]* | wc -l` iLoop=LoopCnt1 while [ $iLoop -le $LoopCnt ] do  if [ -f file.$iCnt ]  then   cat file.$iCnt >> file.all   let iLoop=iLoop+1  fi  let iCnt=iCnt-1 done て具合です。ポイントはme_no_catさんへお願いします。

noname#41382
質問者

補足

アドバイスありがとうございます。 すみません。  iLoop=LoopCnt1 この部分は  iLoop=$LoopCnt ということでしょうか? この部分が不明だったので試していませんが、 これだとfile.xxのxxが最大のファイル名が取得できないような気がするのですが...。 どうでしょうか?

  • me_no_car
  • ベストアンサー率24% (22/90)
回答No.5

普通は#4さんみたいな方が一般的なのかなって 思います。 その為にファイル名例えば file.0001 file.0002 とかってすればそのまま使えます。 とりあえず今回のこんな感じでどうでしょ。 --------------------------------- #!/bin/sh iCnt=1 iLoop=1 LoopCnt=`ls -l file.[0-9]* | wc -l` while [ $iLoop -le $LoopCnt ] do  if [ -f file.$iCnt ]  then   cat file.$iCnt >> file.all   let iLoop=iLoop+1  fi  let iCnt=iCnt+1 done

noname#41382
質問者

お礼

ありがとうございます! 完璧です! 自分もme_no_carさんのような柔軟な頭が欲しいです...^ ^;; みなさんありがとうございました!

noname#41382
質問者

補足

できた!と思って出来たファイルを見たところ重要なことに気が付きました。 ファイルの結合順序が逆だったのです。 file.1 file.2 file.3... ではなく file.20 file.19 file.18....file.1 といった感じで結合しなくてはいけませんでした。 このfile.xxの最大値は決まっていないのでどうにかして取得する必要がありますが その方法が思いつきません。 今回は大きくても10000くらいなので、そこから逆に file -fで探すという方法があると思いますが、 それしか無いのでしょうか? できれば最大値が10桁くらいの数になっても対応できるようにしたいです。 みなさんすみません、ガチガチ頭の私に知恵をかしてください!

  • terra5
  • ベストアンサー率34% (574/1662)
回答No.4

実在するファイルに関して処理すればいいなら、 ファイルのマッチングを使い,全て引数にしてcatに渡せばカウントする必要はないですね。 例えば, cat `ls file.* | sort` >file.all ファイル名を数字に限定したければ、*を適当に置き換える。 例えば, cat `ls file.[0-9]* | sort` >file.all 実は、このままだと問題があります(^^; file.1 file.10 file.11 file.2 というような順番になりますから。

noname#41382
質問者

お礼

アドバイスありがとうございます! >実は、このままだと問題があります(^^; >file.1 file.10 file.11 file.2 というような順番になりますから。 > すみません、実はこれが許されないので... ですが、ファイルのマッチングでの処理には興味があります。 うまく処理できるようなマッチング方法があればいいのですが...。 自分の頭ではterra5さんの方法以外思いつかないです..^ ^;;

  • madman
  • ベストアンサー率24% (612/2465)
回答No.3

私なら、最初のファイル名(数字部分を除いた)を引数にし、ディレクトリ内の同形式ファイルの数を数え、ファイル数でループし、読み出したファイルを順につないでいきます。

noname#41382
質問者

お礼

みなさん、すみません! 条件に誤りがありました! ここの補足に記載いたしました。

noname#41382
質問者

補足

アドバイスありがとうございます。 >私なら、最初のファイル名(数字部分を除いた)を引数にし、 >ディレクトリ内の同形式ファイルの数を数え、ファイル数でループし、読み出したファイルを順につないでいきます。 > #2の方の実践的な例ですね。 これを書いていて気づいたのですが、file.xxのxxは実は連番ではなく、抜けもあるようです。 なのでxxはfile.xxの総数にはならないようです。 なので、xxの最大の数を求めるといった仕組みが必要になりそうです。 ん~自分の頭の中では、かなり複雑な処理になってしまうのですが...。 何か良い案があったら補足願います!

  • rara_sun
  • ベストアンサー率50% (271/539)
回答No.2

実際にシェルスクリプトなるもののソースコードをだしなさいと言われると困ってしまうのですが、アルゴリズム的にこんなのはどうでしょうか? 雰囲気だけと思って下さい。デバックはしてません。 (1) filenum=`ls -l /filedir/ |wc -l` let filenum=filenum - 2(余計な行分だけ引いてください) こんな感じでファイル数を取得 (2) 後は、tk1224さんのソースコードのi=1を i=filenumと書き換えてあげれば・・・ どうでしょうか?

noname#41382
質問者

お礼

アドバイスありがとうございます! >(1) filenum=`ls -l /filedir/ |wc -l` >let filenum=filenum - 2(余計な行分だけ引いてください) > なるほど、雰囲気はつかめました。 後は確実にファイル数を把握するような処理を加えていくといった感じですね。 ありがとうございました。

  • PAPA0427
  • ベストアンサー率22% (559/2488)
回答No.1

シェルスクリプトなら、こんなもんでしょう。 ただ、わたしなら、ループの回数を引数にしますね。

noname#41382
質問者

お礼

早速のアドバイスありがとうございます! >シェルスクリプトなら、こんなもんでしょう。 > そうなんですか? ということはシェル以外で対応する ということでしょうか? >ただ、わたしなら、ループの回数を引数にしますね。 > とりあえず全自動のスクリプトなので、ファイル数をカウントする処理が必要になりますね。