変数コンテンツをシェバンとして使用できますか?

変数コンテンツをシェバンとして使用できますか?

シェル スクリプトにシェバンを追加したいとします。次のように定義された変数があるとします。

SHEBANG="#!/bin/sh"

私の質問は、その変数を次のように別のスクリプトで使用できるかどうかです。

$SHEBANG
# other stuff...

答え1

いいえ。シェバン行はカーネルまたはシステム ライブラリ* によって処理され、シェル変数は処理されません。

  • 何らかの理由で環境変数をインタープリターとして使用したい場合は、 を再生成する「トランポリン」実行可能ファイル (スクリプトではありません) を作成できます。この場合、 を変数から$SHEBANG "$@"省略する必要があります。 は、それが文字通りファイルの最初の 2 バイトである場合にのみ役立ちます。#!#!

  • もう一つの選択肢は、shシェバン行のないスクリプトは(一般的に)呼び出し側のシェルスクリプトとして実行されるという事実。シェバン ターゲットに、シェル構文と重複する便利なコメント構文がある場合は、それを機能させることができます。この戦略は、Lisp インタープリタでは一般的でした。インタープリタも である例の場合、shこれは実行できません。

  • 存在を確信できるなら、その行をさらに推し進めることができますbash。最初の行が

    exec /bin/bash -c 'exec "$SHEBANG" <(tail -n +2 "$0")' "$0" "$@"
    

    ほとんどのインタープリタで動作します。Bashは中間ステップとして使用されるので、プロセス置換使用できるtail最初の行をスキップして、無限ループに陥らないようにします。一部のインタープリタでは、ファイルがシーク可能である必要があり、このようなパイプラインは受け入れられません。


これが本当にやりたいことかどうか、検討する価値はあります。セキュリティ上の懸念は別として、これはあまり役に立ちません。スクリプトのインタープリタをこのように変更できるものはほとんどなく、変更できる場合は、状況に応じて正しいものを生成するより制限されたロジックを使用する方がほぼ確実に良いでしょう (おそらく、実際のスクリプトへのパスを引数としてインタープリタを呼び出すシェル スクリプトを間に挟む)。


* 常にそうとは限りません。特定の状況では、一部のシェルはそれを独自に読み取りますが、それでも変数の展開は行われません。

答え2

これがあなたが考えているユースケースかどうかは分かりませんが、できる行うことは

#!/usr/bin/env python

そのため、スクリプトはをハードコードする代わりにpythonの最初の を使用しますが、これはまたはなどの「適切な」バージョンの Python がインストールされているシステムではうまく動作しない可能性があります。$PATH/usr/bin/python/usr/local/bin/opt

シェバンとして「#!/path/to/NAME」ではなく「#!/usr/bin/env NAME」を使用する方がよいのはなぜですか?


envPython インタープリター (ファイルで呼び出される) は行をコメントとして扱うだけなので、無限ループは発生しません。多くの言語が をコメント文字として使用するため、#!これが設計のポイントのようなものです。#!#

これを拡張するには、多言語プログラムを書くこともできる最初は の下で実行されます#!/bin/shが、その後、実際に使用するインタープリターを判断し、同じスクリプトでそれを呼び出します。

すでに複数行のシェバントリックを使ったウェブページコンパイル言語を含む多くの言語で使用できます (スクリプトは自身の一部をコンパイラに渡し、出力を実行します)。


manperlrunページでは、次の例を次の代替として示しています#!/usr/bin/env perl

    #!/bin/sh
    #! -*-perl-*-
    eval 'exec perl -x -wS $0 ${1+"$@"}'
        if 0;

    your perl script goes here

スクリプトとして実行すると/bin/sh、スクリプト上でeval実行され、引数が渡されます。perl -x -wS

Perl が を実行すると、 は次の行evalの によって制御されるため、 は実行されません。 とは異なり、Perl のステートメントは改行で終了しません。if 0;sh

これをさらに複雑にするには、"$SHEBANG"の代わりにを使用したり、複数のコマンドを実行して使用する Perl インタープリターを選択したりするperlこともできます。sh

答え3

あなたが望んでいるのはこれだと思います:

xb@dnxb:/tmp$ cat /tmp/hole.sh
#!/usr/bin/$shell_var
echo 1
readlink "/proc/$$/exe"
echo 2
function f { echo hello world; } 
echo 3 
xb@dnxb:/tmp$ chmod +x /tmp/hole.sh
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var 
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var 
#!/bin/bash
/bin/dash -- "$@"
xb@dnxb:/tmp$ sudo chmod +x /usr/bin/\$shell_var
xb@dnxb:/tmp$ ./hole.sh
1
/bin/dash
2
./hole.sh: 5: ./hole.sh: function: not found
3
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var 
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var 
#!/bin/bash
/bin/bash -- "$@"
xb@dnxb:/tmp$ ./hole.sh 
1
/bin/bash
2
3
xb@dnxb:/tmp$ 

説明:

  1. \$shell_var必要なシェルを定義するには、ファイルを「変数」として使用します。

  2. readlink "/proc/$$/exe"現在のシェルを印刷します

  3. ダッシュはサポートしていないのでfunction f { echo hello world; }構文そのためエラーが発生していましたfunction: not foundが、ファイル内のシェルを bash に変更すると\$shell_varエラーは発生しなくなります。

  4. 完全なファイル パス (または ./hole.sh が /tmp で実行される場合は相対パス) がファイル$@に渡され\$shell_var/tmp/hole.sh内で実行されます\$shell_var

関連情報