• ベストアンサー

様々な環境で使うことができる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% (4846/10257)
回答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

  • #!/bin/gawk -f ???スクリプトの一行目

    bashのスクリプトの一行目に、環境に合わせて、 #!/bin/gawk -f とか #!/usr/local/bin/bash -f とか書かないとだめですよね。 環境が変わるごとに(bashのパスが変わるごとに)この部分を書き換えるのが面倒なのですが、よい方法はないものでしょうか?

  • gawkの書式について

    gawkについて勉強しています。 最終目的とは違うのですが、動作検証のため、以下のようなスクリプトを書いています。 /usr/local/bin/awk -F, '{system("/usr/bin/echo $1 $2")}' < /dev/console これを実行して、コマンドラインから、test,testと入力すると、 test testと表示させたいのですが、何にも表示されません。 どこがおかしいのでしょうか? ご教授お願いします。

  • シェルスクリプトの記載方法

    linuxでシェルスクリプトを書いています サーバ上に「日付のついた*.bak」ファイルが毎日できています これを3日以上経過した*.bakファイルを消去したいので直接 コマンドラインから次のようにキーボードでたたくと正常に消去できます find /root -name '*.bak' -mtime '+3' -exec rm -f {} \; これをシェルスクリプト(sakujyo.bash)として次のように記載し /rootの中におきました ================(sakujyo.bash)===================== #!/bin/bash find /root -name '*.bak' -mtime '+3' -exec rm -f {} \; =================================================== そして # sakujyo.bash と打鍵すると 次のようにエラーとなります -bash: sakujyo.bash: command not found findのpathが通ってないのかとも考えられるのですが・・・ findコマンドは /usr/bin/ の中にあるので次のように記載してみました =================================================== #!/bin/bash cd /usr/bin find /root -name '*.bak' -mtime '+3' -exec rm -f {} \; ==================================================== このようにしても同じエラーとなります 記載方法等教えていただけませんか よろしくお願いします。

  • /usr/bin/ と /bin/の違い

    gawk の説明のWEBページを見ると、スクリプトを作るときに、最初に書くこととして、 #! /usr/bin/awk -f と説明しているページと #! /bin/awk -f と説明しているページとがあります。 両方大丈夫みたいですが、なぜ、2つともOKなのでしょう?違いは?

  • シェルスクリプトの1行目

    シェルスクリプトの1行目に#!/bin/bashと書くと思うのですが 環境によってbashのパスが違うのですが、そのような場合は2つ書くことは可能でしょうか? 例えばLinuxなんかは#!/bin/bashですよね? Macだと#!/bin/bashだと3系なので古いので5系をインストールすると#!/usr/local/bin/bashになるんです 1台のパソコンで使ってる分には問題ないですが他の環境で使う場合2つ書くにはどうしたら良いでしょうか?

  • シェルスクリプトの1行目に#!を書いてもダメなのはなぜ?

    test.shというシェルスクリプトを #!/bin/bash echo "bash test" のように書き、chmod +x test.shを実行し、 test.shと入力すると、bash testが表示されるはずですが、 エラーbash: test.sh :command not found が出ます。which bashでパスを見ると /bin/bashなので、問題ないはずなんですが。 ちなみに、bash test.shと入力すると、正常に実行されます。 同様のことがperlスクリプト #!/usr/bin/perl print "perl test\n"; についても起こります。 何か設定がまずいのでしょうか? RedHatLinux7.2を使用しています。

  • shellスクリプトに複数の引数を渡したい

    shellスクリプトに複数の引数を渡したいと考えています。 下記のようなperlスクリプトを兼ねる事でやりたいことは達成できました。 これをシェルスクリプトだけでやろうとしていて、ハマってしまい、やり方がわからなかったので、質問しています。 ・echo.shの中身 $ cat echo.sh echo $1 $2 ・imglist.plの中身 $ cat imglist.pl my@imglist= `ls -1 *{.gif,.bmp,.jpg,.jpeg,.png} 2>1&`; foreach(@imglist){ my@name=split /\./,$_; system("bash ./echo.sh $name[0] $name[1]"); } ・実行すると、無事echo.shに引数が2つ渡る。 $ perl imglist.pl abc1 jpg abc10 png abc11 png abc2 jpg abc3 jpg これをシェルのみでやろうとして、はまってしまいました。 以下は、パイプ処理等を使ったりしてやってみたのですが出来ませんでした。 ・awkでファイル名と拡張子に分解し、パイプで渡そうとしても何故か渡らない・・ $ ls -1 *{.gif,.bmp,.jpg,.jpeg,.png} | awk -F . '{print $1,$2}'| bash ./echo.sh ・ファイルに一旦保存し、catコマンドでパイプで渡しても駄目 $ ls -1 *{.gif,.bmp,.jpg,.jpeg,.png} | awk -F . '{print $1,$2}' > imglist.txt $ cat imglist.txt | bash ./echo.sh ・echoコマンドを使ってパイプで渡しても駄目 $ echo `cat imglist.txt` |bash ./echo.sh ・for.shというファイルを作成し、for構文でimglist.txtの中身を渡すと引数が一つずつしか渡らない $ cat for.sh for id in $(cat imglist.txt); do echo $id done $ bash for.sh tile1 jpg tile10 jpg どうやれば、コマンドライン上やシェルスクリプトだけで、今回はファイル名と拡張子という形で複数行からなる2つの引数を渡すことができますでしょうか

    • ベストアンサー
    • Perl
  • sudoでシェルスクリプトを実行させたい

    /usr/bin/sudo を使って、xxx.shを実行させたいので、設定でxxx.shを許可しました。 その後Apacheで動くCGI(perlスクリプト)から、``を使って実行させようと考えています。 このときにこのxxx.shがうまく動きません。多分シェルにアクセスできないのだと思いますが、どうしたらこのスクリプトをroot権限で動かす事ができるようになるのでしょうか。関係あるかどうかわかりませんが、xxx.shの中身はディレクトリ作成コマンドなどです。ネットで検索したら、シェルスクリプトだけ許可にすればよいとあったのですが・・。セキュリティ的には/bin/shを許可するのはまずそうな気がするのですが、こちらを許可してもよいものでしょうか。 環境等 xxx.sh 755 所有者(user1) Apache実行ユーザ (apache) CGIの実行ユーザ(apache?) 挑戦したコマンド(とアパッチのログ) `/usr/bin/sudo sh ./xxx.sh`; ->パスワード求められます (/bin/shの権限がない?) `/usr/bin/sudo ./xxx.sh`; ->/usr/bin/sudo: unable to exec ファイルのパス No such file or directory (xxx.shのファイルの場所はあってます) `/usr/bin/sudo フルパス/xxx.sh`; ->/usr/bin/sudo: unable to exec ファイルのパス No such file or directory (xxx.shのファイルの場所はあってます) 不足の点などあればご指摘ください。

  • Bashシェルスクリプトの書き方

    Bashシェルスクリプトの書き方 ↓のx.shはどこが間違っているのでしょうか? $ cat x.sh #!/bin/bash function x() { local dir if $1; then dir=$1 else dir="." fi `find $dir -type d` } ↓こうなります $ source x.sh $ x dir dir: command not found bash: .: ./dir: はディレクトリです

  • シェルスクリプトのパス

    #!/bin/bash と #!/bin/sh はどのように使い分けるんですか? http://www.ep.sci.hokudai.ac.jp/~inex/y2003/1031/script.html

このQ&Aのポイント
  • printf()関数を使用して、PHPで小数点以下の桁数を指定する方法を説明します。
  • 関数の引数を計算すると、小数点以下の桁数を指定した場合の表示結果が思っていたものと異なることがあります。
  • この方法は、PHPの他の関数でも使用されることがあります。
回答を見る

専門家に質問してみよう