• ベストアンサー

様々な環境で使うことができるgawk スクリプト

awk のスクリプトの一行目に、環境に合わせて、 #!/bin/gawk -f とか #!/usr/local/bin/gawk -f とか書かないとだめですよね。rootの権限を持っていない限り gawk の場所を変えることができませんから。 環境が変わるごとに(bashのパスが変わるごとに)この部分を書き換えるのが面倒なのですが、よい方法はないものでしょうか? という質問をした者です。http://self-solution.okwave.jp/kotaeru.php3?q=1867015 一般ユーザーとして、いろんな環境に対応できるスクリプトができないかと思いました。それで、次の回答に興味があるのですが、 > ちょっとトリッキーで一部制約もありますが、shはまず間違いなく/binにあるので、gawkの場合、 > #!/bin/sh > ZZ==1{ exec sh -c "cat $@ | gawk -f $0" ;} ZZ==1、-c の意味と、sh の前に exec が必要な理由を教えていただけないでしょうか?

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

  • ベストアンサー
  • notnot
  • ベストアンサー率47% (4903/10364)
回答No.3

その回答をしたものです。このスクリプトファイルはまずshが読み込んで実行し、次にgawkが読み込んで実行するため、sh でも gawk でもエラーにならない記述が必要です。1行目の#で始まる行はどちらもコメント行なので問題なし。 gawk で ZZ==1{。。。。} というのはZZという変数が1のときにカッコ内の命令を実行するわけですが、gawkスクリプト内でZZという変数を使っていない限り(使っていればこのZZをスクリプトで使っていない別の文字列にかえればいい)必ず偽で、このカッコ内は実行されません。ただし、実行はされないものの、構文解析はされるので、 ZZ==1{ cat $@ | gawk -f $0 ; exit $?; } などはawkが構文エラーを出します。sh -c ".." で囲むのはgawk的にエラーにしないためです。 exec sh -c "cat $@|gawk -f $0" は、gawkとしても正しい文です。意味は、変数shの値から変数cの値を引き算し、その結果の文字列の前に変数execの中身の文字列、後に"cat $@|gawk -f $0" という文字列を連結した結果の文字列です。 今度は、shが読んだ場合の解釈ですが、ZZ==1{ は、環境変数ZZに、"=1{" という文字列を代入して次のコマンドを実行するという意味。exec は次のコマンドを実行してスクリプトを完了させるということ。sh -c "..." は "..." の内容をshが実行するということ。2行目の}の手前でshスクリプトは実行終了するため、3行目以降はgawkしか読まず、sh的に不正な記述でもOKです。 他に、 ZZ==1{ exec sh -c "gawk -f $0 $@"; } ZZ==1{ sh -c "gawk -f $0 $@" ; exit "$?"; } などでもいいです。前回は思いつきませんでした。場合によっては ZZ==1{ gawk -f $0 "$@" ; exit "$?"; } でもいいです。

white-tiger
質問者

お礼

ありがとうございました。 大変参考になりました!!

すると、全ての回答が全文表示されます。

その他の回答 (2)

  • mac_res
  • ベストアンサー率36% (568/1571)
回答No.2

前回の質問の回答で/usr/bin/envを使うものがあり、bashやperlならうまく行くが、"gawk -f"では、NGという問題がありました。 そのときの議論でminishellを作って、#!lineを空白でchopしてやれば良いというのがありました。 実際作ってみました。root権限が必要ですがコンパイルして/bin/exeとしてinstallしてください。 #! /bin/exe gawk -f #! /bin/exe perl などとコマンドの場所を意識せずに書くことができます。 -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- /* exe - exec a program in the shell script This is GPL progran. */ #include <stdio.h> #include <errno.h> #include <ctype.h> #include <unistd.h> #define MAX_ARGV 100 char *program_name; int main(int argc, char **argv) { char *argv2[MAX_ARGV]; char *p; int i, j; program_name = argv[0]; for (i = 0; i < MAX_ARGV; i++) { argv2[i] = ""; } i = 0; if ((argv2[0] = argv[optind]) != 0) { p = argv2[0]; while (*p != 0) { if (!isspace(*p)) ++p; else { *p = 0; while (isspace(*++p) && *p != '\0'); if (*p != 0) { if (i++ < MAX_ARGV) { argv2[i] = p; } else { fprintf(stderr, "%s: too many args\n", program_name); return (E2BIG); } } } } } for (j = optind + 1; argv[j] != NULL; j++) { argv2[++i] = argv[j]; } argv2[++i] = NULL; execvp(argv2[0], &argv2[0]); perror(argv2[0]); return (errno); }

すると、全ての回答が全文表示されます。
  • mac_res
  • ベストアンサー率36% (568/1571)
回答No.1

このような場合「もしなかったら」を考えると分かりやすいと思います。 ZZ==1 gawk -f $0の記述によりawk script以外にもファイル全体がgawkでscanされます。#!/bin/shの行はawkではコメントとしてパスされますが、ZZ==1の行は実行されます。awkで実行されないように、成り立たない条件分を入れているわけです。 exec sh -c 実行中shellが"cat $@ | gawk -f $0"を実行することを指示しています。execがなければ新たなshellが起動され、実行中のshellはZZ==1の行を実行した後も生きていますので、awk scriptをshell commandとして実行しエラーになります。

すると、全ての回答が全文表示されます。

関連するQ&A