• ベストアンサー

awk or perlが詳しい方ご教授下さい。

お世話になります。 下記のようなファイル内容があるとします。 (あくまでも例です。実際はもっと複雑です。) この内容の大阪府に関する部分だけを抽出したいと思います。 抽出方法はawkで抽出行数を指定する方法を考えていますが、 得られている情報は「大阪府 end」の行数だけです。 「大阪府 start」の行数を取得出来れば目的は達成出来るのですが、 その方法が分かりません。 awkで指定行数から上方向に検索をかけるということは可能でしょうか? (下記例でいくと「大阪府 end」の行数から「大阪府 start」を検索し、 その行数を取得するということをやりたいです。) 可能であればその方法をご教授下さい。 awkで無理ならperlでの方法でも構いません。 <例> ●ファイルの中身 東京都 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 東京都 end 大阪府 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 10000円 大阪府 end 神奈川県 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 20000円 神奈川県 end 北海道 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 3000円 北海道 end ・ ・ ・ 以上、宜しくお願いします。

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

  • ベストアンサー
  • Lean
  • ベストアンサー率72% (435/603)
回答No.4

↓な感じの事をしたいという事? 今、手元にSolarisの環境がないからFreeBSDでの実行です。 %cat data 大阪府 start 淀川支店 AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 10000円 大阪府 end 大阪府 start 天王寺支店 CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 大阪府 end 大阪府 start 住吉支店 AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB DDDDDDDDDDDDDDDDDDD 0円 大阪府 end %cat sample.sh #!/bin/sh branch="$1" datafile=data sedopt=`awk -v Flag=0 ' /start$/{Start=NR} /'${branch}'/{Flag=1} /end$/{if(Flag){printf("%d,%dp",Start,NR);exit}}' ${datafile}` sed -n ${sedopt} ${datafile} %./sample.sh 天王寺支店 大阪府 start 天王寺支店 CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 大阪府 end

hound777
質問者

お礼

ご回答ありがとうございます。 頂いたソースを実行するとエラーとなってしまいます。 現在エラー個所特定中です。 また質問させて下さい。

その他の回答 (11)

  • Lean
  • ベストアンサー率72% (435/603)
回答No.12

>以下の「;Flag=0」はどういう意味なのでしょうか? >{if(Flag){printf("%d,%d ",Start,NR);Flag=0}} 下記、実行例のように「天王寺支店」を含む「~start」「~end」の組が複数存在する場合に対応するためです。 引数で指定した支店名を見つけるとFlagに1を設定しますので、「~end」を見つけた後、次の一致するものを探すために初期化しています。 複数存在する事はないという事なら、このままだと見つけた以降も余計な読み込みが発生しますので exit で終了にしていいと思います。 >% ./sample.sh 天王寺支店 1つ目の「大阪府 start」「大阪府 end」組 >大阪府 start >天王寺支店 >CCCCCCCCCCCCCCCCCCC >DDDDDDDDDDDDDDDDDDD >1000円 >大阪府 end 2つ目の「大阪府 start」「大阪府 end」組 >大阪府 start >天王寺支店 >EEEEEEEEEEEEEEEEEEE >FFFFFFFFFFFFFFFFFFF >1500円 >大阪府 end

hound777
質問者

お礼

初期化ですか。理解しました。 ありがとうございます。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.11

#6です。 > awk: 構文エラー (3 行目の周辺) > awk: 不正な文を検出しました。 (3 行目の周辺) > awk: 構文エラー (8 行目の周辺) > awk: 不正な文を検出しました。 (8 行目の周辺) > awk: 構文エラー (10 行目の周辺) > awk: 不正な文を検出しました。 (10 行目の周辺) ふうむif文全部引っかかってますね。一応gawkの awk 互換モードで チェックしたので構文的な問題はないはずなんですが… とりあえず、nawk(Solarisにはこれもあるはず)ではどうでしょうか? あと、if文を if (/^大阪府 start$/) { cnt=0 } if (/^大阪府 end$/ && exit_flag==1) { exit(0) } if (/^天王寺支店$/) { exit_flag = 1 } のようにブレースで本体を囲むと状況は変わりますか?

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.10

#6,8 です。 > awk: 構文エラー (1 行目の周辺) > awk: 処理を中止します。 (1 行目の周辺) というエラーとのことですが #!/usr/bin/awk /^大阪府 start$/, /^大阪府 end$/ { 多分、#の行を #!/usr/bin/awk -f のように -f を末尾につければ解決するのではないでしょうか。 すいません前回見落としてました。

hound777
質問者

お礼

ご回答ありがとうございます。 -fを付けたところ以前出力したエラーが表示されました。 if文のところがエラーになります。何故でしょうか? <エラー> awk: 構文エラー (3 行目の周辺) awk: 不正な文を検出しました。 (3 行目の周辺) awk: 構文エラー (8 行目の周辺) awk: 不正な文を検出しました。 (8 行目の周辺) awk: 構文エラー (10 行目の周辺) awk: 不正な文を検出しました。 (10 行目の周辺)

  • xjd
  • ベストアンサー率63% (1021/1612)
回答No.9

こんばんは。 回答のほとんどが、awkなので、ためしに、Perl で作成してみました。 よろしかったら、参考にしてください。 # cat data.pl #!/usr/bin/perl @line = (); while (<>) { # 標準入力より1レコード読む   if ( /^大阪府 start$/ .. /^大阪府 end$/ ) { # 大阪府ブロックの中の時     push(@line, $_); # 大阪府ブロックの1レコードを配列変数に追加     if ( /^大阪府 end$/ ) { # 大阪府ブロックが終了の時       if ($line[1] =~ /天王寺支店/) { # 大阪府ブロックの2レコード目が天王寺支店なら         print @line;         print "***** 行数 = ", $#line+1, "\n";       }       @line = (); # 配列変数の初期化     }   } } # ./data.pl < data.txt 大阪府 start 天王寺支店 CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 大阪府 end ***** 行数 = 6 配列変数(@line) には、ブロックの各レコードが1行ずつ代入されているので、 必要なレコードを取り出して処理できます。 配列変数(@line)の中身 $line[0] = 大阪府 start $line[1] = 天王寺支店 $line[2] = CCCCCCCCCCCCCCCCCCC ...... $line[$#line-1] = 1000円 $line[$#line] = 大阪府 end ($#line は、配列変数@line の添え字の最大値を返します。)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.8

#6 です。 if文でエラーになるとのことですが、どんなエラーでしょう? 構文エラーですか? Solrarisを使える環境にないのでそのawkでは試していませんが、 一応動作も確認しているのですが。 if ($0 ~ /大阪府 start/ && ...) のような形に修正したら エラーは消えますか? エラーのままでも、補足にメッセージそのまま貼り付けてください。

hound777
質問者

お礼

ご回答ありがとうございます。 if文でエラーになる現象を再現できませんでした。 すいません。 ご提供頂いたスクリプトを実行すると以下のエラーが発生します。 awk: 構文エラー (1 行目の周辺) awk: 処理を中止します。 (1 行目の周辺) ご回答の程よろしくお願いします。

  • Lean
  • ベストアンサー率72% (435/603)
回答No.7

No.4です。 /usr/bin/awk(/bin/awk)は-vオプションがないみたいなのでエラーになるようです。 少し修正して、Solaris 10(x86版)で実行してみました。 >(1)ファイルから"天王寺支店"が何行目にあるか調べる >(2)(1)の結果の行数から上下に検索を行い、"大阪府 start"と"大阪府 end"が直近の何行目にあるか調べる。 に該当する部分がawkの所。 考え方は、No.5の方が書いた通り。 >(3)(2)の結果の行数を指定して"天王寺支店"の文字を含む"大阪府 start~大阪府 end"のくくりを抜き出す の部分がsedの部分。 ちなみに↓のスクリプトですが動作すればいいや程度のものですので。 % uname -a SunOS Solaris10 5.10 Generic_118855-14 i86pc i386 i86pc % cat data 大阪府 start 淀川支店 AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 10000円 大阪府 end 大阪府 start 天王寺支店 CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 大阪府 end 大阪府 start 住吉支店 AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB DDDDDDDDDDDDDDDDDDD 0円 大阪府 end 大阪府 start 天王寺支店 EEEEEEEEEEEEEEEEEEE FFFFFFFFFFFFFFFFFFF 1500円 大阪府 end % cat sample.sh #!/bin/sh branch="$1" datafile=data linelist=`awk ' BEGIN{Flag=0} /start$/{Start=NR} /'${branch}'/{Flag=1} /end$/{if(Flag){printf("%d,%d ",Start,NR);Flag=0}}' ${datafile}` for opt in ${linelist} do sed -n ${opt}p ${datafile} done % ./sample.sh 天王寺支店 大阪府 start 天王寺支店 CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 大阪府 end 大阪府 start 天王寺支店 EEEEEEEEEEEEEEEEEEE FFFFFFFFFFFFFFFFFFF 1500円 大阪府 end

hound777
質問者

お礼

ご回答ありがとうございます。 希望結果が出力出来そうです。 ありがとうございました。

hound777
質問者

補足

すいません。 再び質問させて下さい。 以下の「;Flag=0」はどういう意味なのでしょうか? {if(Flag){printf("%d,%d ",Start,NR);Flag=0}}

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.6

awkだけで片付けてみました。 start と end の行も出力したいのなら、コメントにしてある行を 使うようにしてください。 #!/usr/bin/awk /^大阪府 start$/, /^大阪府 end$/ {   if (/^大阪府 start$/)     cnt=0   data[cnt++] = $0   if (/^大阪府 end$/ && exit_flag==1)     exit(0)   if (/^天王寺支店$/)     exit_flag = 1 } END {   #for (i=0; i<cnt; i++)   for (i=1; i<cnt-1; i++)     printf "%2d:%s\n", i, data[i] } 支店のデータが巨大で配列に収められない場合でもできますが それはまたのお楽しみに。 POSIXのawkなら途中でexitしてもENDブロックを実行するはずですが、 ひょっとしたらSolarisのawkはそうでないかも? そのときは適当にフラグを見て読み飛ばすなどしてください。

hound777
質問者

お礼

ご回答ありがとうございます。 本スクリプトを実行するとif文のところでエラーとなってしまいます。 何故でしょうか?

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.5

天王寺支店の上下に大阪府startと大阪府endが必ずあるということでしたら、 先頭から順に読んでいき 1.大阪府startの行を記憶する。 新しい大阪府startがでたら、次々と置き換える。 2.天王寺支店が、出現したら、 最後の大阪府startの行を採用する。 3.以降は、大阪府endを探して終わり。 でどうでしょうか。

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

えぇと.... 「下記例でいくと「大阪府 end」の行数から「大阪府 start」を検索し、その行数を取得するということをやりたいです。」 という文章の意味がわかりません. 挙げられたデータに対して, どのような出力がほしいのかをもっとわかりやすく (できれば具体的に) 示していただけませんか?

hound777
質問者

お礼

ご確認ありがとうございます。 例をちょっと変えてみました。 (ファイルの中身には「大阪府 start~大阪府 end」という一定の法則性があります。 しかし、このくくりの中の内容はそれぞれ異なります。支店名(重複無し)が違ったり、レコード数が違ったり) やりたいことは「"天王寺支店"の文字を含む"大阪府 start~大阪府 end"のくくりを抜き出す」 ということです。 自分なりに考えた抜き出し方はこうです。  (1)ファイルから"天王寺支店"が何行目にあるか調べる  (2)(1)の結果の行数から上下に検索を行い、"大阪府 start"と"大阪府 end"が直近の何行目にあるか調べる。  (3)(2)の結果の行数を指定して"天王寺支店"の文字を含む"大阪府 start~大阪府 end"のくくりを抜き出す 上記(2)の方法がどうしても分かりません。awkで出来れば最高なのですが。 <例> 大阪府 start 淀川支店 AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 10000円 大阪府 end 大阪府 start 天王寺支店 CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 大阪府 end 大阪府 start 住吉支店 AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB DDDDDDDDDDDDDDDDDDD 0円 大阪府 end 以上、よろしくお願いします。

  • Lean
  • ベストアンサー率72% (435/603)
回答No.2

http://oshiete1.goo.ne.jp/kotaeru.php3?q=2348178 ↑で質問した内容と同じように思えるのですが、その内容と今回は違うという事ですか? あと、回答がありましたが、その時の質問の回答としては希望の事は出来なかったという事でしょうか?

hound777
質問者

お礼

ご認識の通りです。 上記にある (下記例でいくと「大阪府 end」の行数から「大阪府 start」を検索し、 その行数を取得するということをやりたいです。) ということがやりたいのですが分かりません。

関連するQ&A

  • Bシェルでファイルから特定の範囲を抽出するには?

    お世話になります。 ディレクトリにファイル群があるとします。 そのファイル群の中の特定のファイルの特定の個所を抽出し、別ファイルに吐くというシェルを組みたいのですが、 どのようにしたら良いでしょうか? <例> ●ファイル群は作成された日付を持ちます。 売上_200601.txt、売上_200602.txt、売上_2006003.txt、売上_200604.txt・・・・・・ ●ファイルの中身はある法則が成立しています。 (売上_20060101.txtの中身) 東京都 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 1000円 東京都 end 大阪府 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 10000円 大阪府 end 神奈川県 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 20000円 神奈川県 end 北海道 start AAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCC DDDDDDDDDDDDDDDDDDD 3000円 北海道 end ・ ・ ・ ※都道府県の重複はありません 月毎の売上が纏められているファイルの中から特定の年月の都道府県売上分を抽出したいです。 抽出条件としましては「年月」「都道府県」が与えられます。 上記の例でいくと、「売上_200601.txt」の「東京都 start~東京都 end」までを抽出したいのです。 ファイルの特定は抽出条件の「年月」を元にfindコマンドにて導き出すことは可能なのですが、 中身の「東京都 start~東京都 end」をどのように抽出するのかが分かりません。 分かり難い内容に恐縮ですが、ご教授の程よろしくお願いします。

  • AWKスクリプト作成法200606

    AWKでのアルゴリズムについての質問です。 やりたいことはあるファイルの中にキーワードとなる文字列を含む行から ”END”という文字列の行までをひとかたまりとして,それを指定された順序 に並び替えたい。 例 元ファイル ・・・KEY1・・・ : END ・・・KEY2・・・ : END ・・・KEY3・・・ : END 順序指定ファイル KEY3 KEY1 KEY2 出力ファイル ・・・KEY3・・・ : END ・・・KEY1・・・ : END ・・・KEY2・・・ : あるデータのかたまりを並び替える方法がどうもひらめきません。 いい方法というかアルゴリズムを教えて下さい。

  • 警察は北海道は「北海道警」大阪は「大阪府警」神奈川は「神奈川県警」であ

    警察は北海道は「北海道警」大阪は「大阪府警」神奈川は「神奈川県警」であるが、東京は「東京都警」ではなく「警視庁」である。 なぜ東京だけ違うのか?

  • なぜ東京だけ違う?

    警察は、北海道は「北海道警」大阪は「大阪府警」神奈川は「神奈川県警」だが、東京は「東京都警」ではなく「警視庁」である。 なぜ東京だけ違うのか?

  • 指定した文字列を含む行から、指定した文字列を

    含む行まで、全ての行を抽出して別ファイル(名前は元ファイルと同じ)としたいのです。 対象ファイルに結果を上書きでもかまいません。 指定したフォルダの中にある複数のフォルダ各々に1つずつ入っているテキストファイルが対象になります。 例) 東京都 新宿区 <start123AAA> 東京都 港区 <end>AAABBB 東京都 目黒区 どのファイルにも必ず<start と<end の文字列があります。(必ず1組です) <start の文字列を含む行から <end の文字列を含む行までを 抽出したいのです。 (欲しい結果↓) <start123AAA> 東京都 港区 <end>AAABBB フリーのエディタソフトのgrep、grepのマクロ、バッチでできないものかと試してみているのですが どうしてもうまくいきません。 抽出したい範囲、削除する範囲には禁則文字?が入っているのでバッチは難しいのでしょうか? できればバッチで処理できればなと思っているのですが、うまくいきません。 (<>!""/ あたりが入っています。別の文字に置き換えることは可能です) 範囲を抽出ではなく、 <startを含む行の一つ上の行から前を全て削除 <endを含む行の一つしたの行から後を全て削除 する方法や VBAで、スタート行をとエンド行をSearchして抽出する方法など ご教授いただきたくお願い致します。

  • データの個数を数える

    データの個数を数えるにはどのようにしたら良いのでしょうか? たとえば、クエリに 県名 東京都 東京都 大阪府 宮城県 北海道 大阪府 千葉県 東京都 とあったとして、 各県名がそれぞれ何件ずつあるか知りたいのですが、方法がさっぱりわかりません。すべての都道府県を表示し、さらにデータにない県名は「0件」としたいのです。 たとえば、上の例を使うと 北海道 1 青森県 0 宮城県 1 東京都 3 大阪府 2 :::略::: 沖縄県 0 としたいと考えています。access 2003を使用しています。 ご教授よろしくお願いします。

  • unix awkコマンド 複数区切り

    カンマ2つ(,,)+文字の抽出方法がわかりません・・・。 例: ファイル内(下記2行)に対して 12,34,,567@89 1,2,34,,567@89 1行目2行目ともに「567」が表示したいです。 # ”カンマ2つを一つの文字列”だけだと「awk -F",,"」でいけますが、+文字列がどうやってもできません。。

  • MySQL SELECT WHERE 条件 大量

    mysqlのSELECT文でWHEREを使い条件を指定する時、指定する条件が数十件~数百件合った場合どうすればよいのでしょうか? 例えば、 以下のような感じで名前カラムと住所カラムがあったとします name  address 田中  東京都 鈴木  大阪府 佐藤  北海道 加藤  沖縄県 中村  群馬県 小林  岐阜県 高橋  東京都 渡辺  大阪府 伊藤  北海道 山本  沖縄県 斎藤  群馬県 阿部  岐阜県     ・     ・     ・ そこで、東京都と大阪府と北海道の人だけをSELECT文で選択したいのですが、 WHERE `address` = '東京都' OR `address` = '大阪府' OR `address` = '北海道' というように、一つづつ記述していかなければならないのでしょうか? この例の場合だと3件だけの指定ですが、もしもっと膨大な量を指定したいとなったとき、とても効率が悪いように思います。 なにか良い方法はないのでしょうか? ちなみにPHPからMySQLを利用しているのですが、現在 $where = array(); foreach($address as $value){ $where[] = "`address` = '${value}'"; } $where = implode(' OR ', $where); としておいて、 mysql_query("SELECT * FROM table WHERE ${where}"); とする方法しか思いつきません。 もっとスマートな方法があれば教えて下さい。よろしくお願いします。

    • ベストアンサー
    • MySQL
  • 豊川稲荷の別院を教えてください

    豊川稲荷の別院が、北海道、東京都、神奈川県、静岡県、大阪府、福岡県にあるとのことですが、それぞれの寺院名を教えてください。

  • Bシェルのawkコマンドについて

    Bシェルのawkコマンドについてですが、例えばプロンプトから % ls -l hoge.txt | awk '{print $5}' と入力すると、hoge.txtのファイルサイズが出力されますが、同様のことをBシェルの中で行おうと思っています。 下記のように、配列arrayにはスペース区切りで3つのデータが入っており、N番目のデータを取得したいという場合に、Nに変数を使用して取得する方法がわかりません。 #!/bin/sh array="AA BB CC" num=3 # CCを取得したい echo $array | awk '{print $num}' 上記で実行すると、"AA BB CC"のようにすべて出力されてしまいます。awkで出力する箇所を${数値}で指定すればうまくいくのですが、ランダムで決めた数値(1~3)をnumに入れて取得したい場合、このようなことは可能でしょうか。 もし不可能な場合でも、awk以外に何かコマンドがあればご教授願います。なお、作成するのはBシェルになります。