- ベストアンサー
awkで、キーボードからファイルを指定するには
私は、awkプログラムを始めたばかりの初心者です。 データファイルがfile1、file2、file3とあって、どのファイルを実行させたいかをキーボードから入力させるには、どのようにすればよいのでしょうか。 次のようなプログラムを作ったのですが、うまくいきません。 { printf "ファイル名?" > "/dev/stderr"#画面表示 getline < "/dev/stdin" #数字入力 aaa = $0 #aaaに代入 } { getline < ("file"aaa".txt") #ファイル入力 print $0 #レコードを出力 } これを、バッジファイルで次のように実行をかけて、「結果.txt」に出力する。 jgawk -f prog.awk con > 結果.txt すると、コマンド画面が消えず、エラーとなってしまいます。 バッジファイルに問題があるのでしょうか。 「-f」の後には、プログラム名、データ名>出力ファイル名 とやるようですが、この場合、データ名が決まっていないわけで、キーボード入力したいのだから、「con」でいいのでしょうか。 プログラムを実行させるのに、ほかに良い方法があるのでしょうか。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。補足をありがとうございます。 個人的には awk の getline の文法は好きではありません。 getline を使う必要に迫られたら、別の言語を使うことも 視野に入れておいてください。 >継続条件式である最初の「(getline)」は、どういう意味なのでしょうか。 最初の getline は「(getline < "/dev/stdin")」と書くのと同じですが、 「ファイル名は?」に対する入力($0)を標準入力から読み込むものです。 それを元に次の行で読み込むべきファイル名(infile)を組み立てています。 プロンプトに対して「^Z」(Ctrl+z, UNIX/Linux 環境なら「^D」) を入力すれば外側の while から抜け、スクリプトの実行が終了します。 >2つ目のwhile文の「 (getline < infile > 0)」の意味もよく分かりません。 >この部分で指定したファイルのデータを読み込んでいるのでしょうか。 そういうことです。 「((getline < infile) > 0)」の方がわかりやすかったかもしれませんね。 infile から正常に1行ずつ読み込める限り getline は1を返します。 一方、ファイルの終わりに達したら0が返るので while は終了します。 また、infile がもともと存在しないファイルであれば-1が返り、 while の中は実行されません。 おわかりになりましたでしょうか。回答者さんへのお返事は公平にお願いします。 それでは。
その他の回答 (4)
- nightowl
- ベストアンサー率44% (490/1101)
No.2 です。 >#2さんのプログラムで、close(infile)を2行後ろに移動させれば、動くと思います。今のままだと1行目を表示し続ける。 あ、本当だ。1行だけのファイルでしかテストしてなかったので 破綻に気づきませんでした。ご指摘感謝します。 以下、修正版です。これで複数行からなるファイルの中身も書き出してくれるはず。 (HP-200LX/MS-DOS 上の jgawk で確認。古っ!) function prompt(message) { printf("%s", message) > "/dev/stderr" } BEGIN { prompt("ファイル名? ") while (getline) { infile = "file" $0 ".txt" while (getline < infile > 0) { print } close(infile) prompt("ファイル名? ") } }
補足
whileが2度使われています。 「while」の意味は分かるのですが、継続条件式である最初の「(getline)」は、どういう意味なのでしょうか。2つ目のwhile文の「 (getline < infile > 0)」の意味もよく分かりません。 この部分で指定したファイルのデータを読み込んでいるのでしょうか。
- notnot
- ベストアンサー率47% (4900/10358)
#2さんのプログラムで、close(infile)を2行後ろに移動させれば、動くと思います。今のままだと1行目を表示し続ける。 ただ、Windowsのバッチでこういうことをするなら、入力はバッチ機能でやるほうが良いと思います。Win2000/XPとすると、 set /p N=ファイル名? jgawk "{print $0}" file%N%.txt > 結果.txt おそらく実際はprint $0でなくもっと複雑な処理なんでしょうから、prog.awk にそれを書いて set /p N=ファイル名? jgawk -f pgm.awk file%N%.txt > 結果.txt Win9x/Meだと set /p 機能が無いので一時ファイルを作ったりせざるを得ないためかえって複雑になり、#2さんのプログラムの上記改訂版を使うのがいいかと思います。
お礼
「set /p 」でバッチ機能を使うやり方なんて全く知らなかったです。大変参考になりました。ありがとうございました。
- nightowl
- ベストアンサー率44% (490/1101)
こんばんは。 あいにく Windows のバッチファイルから動かせる環境にはないのですが、 terra5 さんのスクリプトをもとに getline でループさせ、 若干のエラーチェックを加えてみました。以下がスクリプトの内容です。 function prompt(message) { printf("%s", message) > "/dev/stderr" } BEGIN { prompt("ファイル名? ") while (getline) { infile = "file" $0 ".txt" if (getline < infile > 0) { print close(infile) } prompt("ファイル名? ") } } 2番目の getline で暗黙のうちにオープンされたファイルを 明示的にクローズしてやらないと次回同じファイルにアクセスした時に 内容が読めなくなるようです(ファイルポインタが末尾に留まっているため?)。
- terra5
- ベストアンサー率34% (574/1662)
{}はなんらかの入力行があって初めて動作しますので、 これを実行すると標準入力の指定が無いため、 キーボードからの入力待ちになります。 とくに、入力を必要としない場合は BEGIN {}の中に記述します。 BEGIN { printf "ファイル名?" > "/dev/stderr"#画面表示 getline < "/dev/stdin" #数字入力 aaa = $0 #aaaに代入 getline < ("file" aaa ".txt") #ファイル入力 print $0 #レコードを出力 } ファイルからの入力が複数の場合は getlineでループしてください。
お礼
「BEGIN」ですね。参考になりました。ありがとうございました。
お礼
大変ためになりました。おかげで、問題が解決できました。ありがとうございました。