awkの複数の引数をシバン(つまり#!)で使用するにはどうすればよいですか?


118

シバンを使ってgawkスクリプトを実行したいの--re-intervalですが。の「素朴な」アプローチ

#!/usr/bin/gawk --re-interval -f
... awk script goes here

gawkは最初の引数"--re-interval -f"(空白で分割されていない)で呼び出されるため、機能しません。そのための回避策はありますか?

もちろん、gawkを直接呼び出すことはできませんが、最初の引数を分割するシェルスクリプトにラップするか、またはgawkを呼び出してスクリプトを別のファイルに入れるシェルスクリプトを作成できますが、実行する方法があるかどうか疑問に思いましたこれは1つのファイル内にあります。

シバン行の動作はシステムごとに異なります-少なくともCygwinでは、引数を空白で分割しません。私はそのように動作するシステムでそれをどのように行うかを気にしているだけです。スクリプトは移植可能であることを意図していません。


1
私が今行ったばかげた実験は、shebang行で別のスクリプトを使用するスクリプトを使用することでした。これにより、引数が正しく分割されました。
Hasturkun 2010年

@Hasturkun、これは別の問題を引き起こします。シバン行の動作も、呼び出されたプログラム自体がスクリプトであるかどうかにかかわらず、システムごとに異なります。
dubiousjim


最近のバージョンのgawk(> = 4.0)では、--re-intervalもう必要ありません([ gnu.org/software/gawk/manual/…を参照)。

回答:


25

これは(g)awkで私にはうまくいくようです。

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

#!ランを/bin/sh、このスクリプトは、第1のシェルスクリプトとして解釈されるように、。

最初、私は単にを試しました"exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"が、awkはそれをコマンドとして扱い、無条件にすべての入力行を出力しました。それが私が入れた理由ですarbitrary_long_name==0-それは常に失敗するはずです。意味不明な文字列に置き換えることができます。基本的に、私はawkでシェルスクリプトに悪影響を及ぼさない誤った条件を探していました。

シェルスクリプトでは、arbitrary_long_name==0は呼び出される変数を定義し、にarbitrary_long_name設定し=0ます。


これは私の答えですが、十分に移植性があり堅牢であるかどうか疑問に思います。特にに依存していますかbash、それともPOSIXで動作しますshか?そして、私はawk頻繁に使用しないので、2行目の私のトリックがその行を強制的awkに無視する良い方法であるかどうかはわかりません。
アーロンマクデイド2014

私が思っていたのは+1ですが、おそらくお勧めできません(したがって、相対投票)
アーロンホール

@AaronHall、これがどんな問題を抱えているのか説明できますか?変数arbitrary_long_nameが実際のawkプログラムで使用される変数と競合しない限り、問題は発生しません。行方不明のものはありますか?
Aaron McDaid

#!/bin/sh -代わりに#!/bin/shを使用して-、最初の文字が0の引数で呼び出された場合にスクリプトが危険な方法で誤動作する可能性を防ぎます。これは、Cなどのプログラミング言語で偶発的に発生する可能性があり、引数の配列の一部として呼び出されたプログラム名execveを同様の関数に渡すのを忘れることにより、誤って混乱することが簡単です。攻撃者がインタラクティブなシェルを取得できるようにする悪意のある悪用可能な脆弱性の最後のステップとなる。
mtraceur

161

シバンラインは、POSIX、SUS、LSB、またはその他の仕様の一部として指定されたことはありません。私の知る限り、それは適切に文書化されていません。

間のすべてを取る:それはないかについてのラフコンセンサスがある!\nし、execそれが。仮定は間のすべてです!と、\nインタプリタへの完全絶対パスです。空白が含まれているとどうなるかについての合意はありません。

  1. 一部のオペレーティングシステムは、単に全体をパスとして扱います。結局のところ、ほとんどのオペレーティングシステムでは、パス内の空白やダッシュは正当です。
  2. 一部のオペレーティングシステムは空白で分割され、最初の部分をインタープリターへのパスとして扱い、残りを個別の引数として扱います。
  3. 一部のオペレーティングシステムは、最初の空白で分割され、前部をinterpeterへのパスとして扱い、残りを単一の引数として扱います(これは表示されているものです)。
  4. 一部ではシェバング行をサポートしていませんまったく

ありがたいことに、1。と4.は消滅したように見えますが、3。はかなり広まっているため、複数の引数を渡すことができるとは限りません。

コマンドの場所もPOSIXまたはSUSで指定されていないので、あなたは一般的に実行可能ファイルの受け渡しによって、単一の引数ことを使い切る名前をするenvように、それが実行可能ファイルの場所を決定することができます。例えば:

#!/usr/bin/env gawk

[明らかに、これはまだ特定のパスを前提としていますがenv、それが存在するシステムは非常に少ない/binため、これは一般的に安全です。場所は、envより多くの場所よりも、標準化されているgawkなど、またはさらに悪い何かpythonrubyまたはspidermonkey。]

これはあなたが実際に使用することができないことを意味任意の引数をまったく


1
FreeBSDのenvには-Sここで役立つスイッチがありますが、私のLinux envにはありません。また、gygwinでも使用できないと思います。@hstoerr、状況が異なる他のユーザーが後で質問を読んでいる可能性があるため、移植性が不要になったとしても、一般に移植可能な回答が望ましいです。
dubiousjim

4
したがって、シバンで引数を移植可能に使用することはできません。しかし、必要な手段で引数が必要な場合はどうでしょうか?私は解決策を含むラッパーシェルスクリプト書き込みにあると推測している#!/bin/shとします/usr/bin/env gawk --re-interval -f my-script.awk。あれは正しいですか?
ロリーオケイン

1
私は同意しない。1つの引数を移植性の高い方法で使用できます。引数を使用できないシステムは、この従来のUnixismの実装に無残に失敗します。これがハッシュバンです。非実装が公平なゲームである場合、#!それ自体は移植可能ではないと言えます。たとえば、Windowsはこの規則を「自然に」まったく認識しません。引数が1つの場合、Unixではこれを実行できるようにする必要があります#!/usr/bin/awk -f
Kaz

7
@カズ:はい、しかし、多くのバイナリのパスは標準化されていないため、1つの引数などを使い果たします#!/usr/bin/env ruby
イェルクWミッターク

3
@Pacerier:POSIX仕様を変更し、すべてのシステムが仕様に準拠するように更新されるまで20〜30年待ちます。
イェルクWミッターク

18

正確には移植可能ではありませんが、coreutils 8.30以降、およびそのドキュメントによれば、次のように使用できます。

#!/usr/bin/env -S command arg1 arg2 ...

だから与えられた:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

あなたは得るでしょう:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

そしてあなたが興味showargsがある場合は:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

元の答えはこちら


1
ちなみに、FreeBSDには-Sが何年も(6.0以降)ありました。これは、coreutilsへの移植性の追加です。
フアン

12

私は同じ問題に遭遇しましたが、(少なくともLinuxでは)シバンで空白が処理される方法のため、明確な解決策はありませんでした。

ただし、いくつかのオプションは、それらが短いオプションであり、それらを連結できる(GNUの方法)限り、シバンで渡すことができます。

たとえば、

#!/usr/bin/foo -i -f

しかし、あなたは持つことができます

#!/usr/bin/foo -if

明らかに、これはオプションに同等のものがあり、引数を取らない場合にのみ機能します。


11

CygwinとLinuxでは、シバンのパス以降のすべてが1つの引数としてプログラムに解析されます。

awkシバン内の別のスクリプトを使用して、これをハッキングすることが可能です。

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

これは{system("/usr/bin/gawk --re-interval -f " FILENAME); exit}awkで実行されます。
そして、これは/usr/bin/gawk --re-interval -f path/to/your/script.awkシステムシェルで実行されます。


2
スクリプトに引数を渡した場合、これは機能しません
Steven Penny

4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

上記のシェルシバントリックは、より移植性があり/usr/bin/envます。


'' ':'はホールドオーバーです。私の元の解決策はpythonスクリプト用であったため、 '' ':'はpythonインタープリターにexec部分を無視するように指示します。
user3123730 2014年

4
あなたの解決策はのためにあなたは反対票を投じていると思いますpythonが、この質問はについてawkです。
アーロンマクデイド2014

1
Pythonの素晴らしいハック。
Zaar Hai

3

gawkのマニュアル(http://www.gnu.org/manual/gawk/gawk.html)のセクション1.14の最後では、shebangの行からgawkを実行する場合は単一の引数のみを使用する必要があることに注意してください。OSはgawkへのパス以降のすべてを単一の引数として扱うと述べています。おそらく、--re-intervalオプションを指定する別の方法がありますか?おそらく、スクリプトがシバン行でシェルを参照gawkし、コマンドとして実行し、スクリプトのテキストを「ヒアドキュメント」として含めることができます。


オプションを指定する他の方法がないようです。あなたは正しい:gawk -f-<< EOF、いくつかのスクリプト行、EOFは機能しますが、gawkで標準入力を読み取ることができません。
Hans-PeterStörr2010年

hereドキュメントはの標準入力ストリームをgawk使い果たしますが、stderrを介してパイプで何かを送ることができる場合があります(つまり、このスクリプトにパイプする前にstdoutをstderrにリダイレクトします)。私は実際にそれを試したことはありませんが、最初のプロセスがstderrで何も出力しない限り、動作する可能性があります。他に何も使用していないことを確認したい場合は、名前付きパイプ(linuxjournal.com/content/using-named-pipes-fifos-bash)を作成することもできます。
bta

3

bashand gawk自体を使用して、シバンをスキップし、スクリプトを読み取り、それをファイルとして2番目のインスタンスに渡しますgawk [--with-whatever-number-of-params-you-need]か?

#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
{
  print "Program body goes here"
  print $1
}

(-同じことは自然にeg sedtailで達成することもできますがbashgawkそれ自体にのみ依存するある種の美しさがあると思います;)


0

おもしろい:ファイル記述子3と4を介してstdinとプログラムを再ルーティングする次の非常に奇妙な解決策があります。スクリプト用の一時ファイルを作成することもできます。

#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN {print "HALLO"}
{print \$1}
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3

シェルがスクリプトで変数展開を行うので、1つ問題があります。そのため、$(スクリプトの2行目で行ったように)と、それ以上の$を引用符で囲む必要があります。


-1

ポータブルソリューションの場合awkは、gawkではなくを使用/bin/shして、シバンで標準のBOURNEシェル()を呼び出し、awk直接呼び出して、stdin経由ではなく、ヒアドキュメントとしてコマンドラインでプログラムを渡します。

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

注:へ-f引数はありませんawk。これによりstdinawkから入力を読み取ることができます。をgawkインストールしてにインストールPATHすると、元の例でしようとしていたと私が思うすべてが達成されます(ファイルの内容を入力ではなくawkスクリプトにしたい場合、シェバンアプローチはそれを次のように処理したと思います) )。


3
それは私にはうまくいきませんでした。バッシュマンは<<< blablaはstblaにblablaを置くと言います。もしかして<<-EOF?いずれにせよ、それはまたプログラムを標準入力に置きます。
Hans-PeterStörr2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.