web-dev-qa-db-ja.com

シバンの複数の引数

Shebang行を介して複数のオプションを実行可能ファイルに渡す一般的な方法があるかどうか疑問に思っています(#!)。

私はNixOSを使用しており、私が書いたスクリプトのシバンの最初の部分は通常/usr/bin/env。私が遭遇する問題は、その後に続くすべてがシステムによって単一のファイルまたはディレクトリとして解釈されることです。

たとえば、posixモードでbashによって実行されるスクリプトを記述したいとします。シバンを書く素朴な方法は次のようになります:

#!/usr/bin/env bash --posix

しかし、結果のスクリプトを実行しようとすると、次のエラーが発生します。

/usr/bin/env: ‘bash --posix’: No such file or directory

私は この投稿 を知っていますが、より一般的で明確な解決策があるかどうか疑問に思っていました。


[〜#〜] edit [〜#〜]Guile スクリプトの場合、達成する方法があることを知っていますマニュアルの セクション4.3.4 に記載されているもの:

 #!/usr/bin/env sh
 exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
 !#

ここでの秘訣は、2行目(execで始まる)がshによってコードとして解釈されることですが、#! ... !#ブロック、コメントとして、したがって、ガイルインタープリターによって無視されます。

この方法を通訳者に一般化することは不可能でしょうか?


2番目のEDIT:少し遊んだ後、stdinから入力を読み取ることができるインタープリターの場合、次のメソッドのようですうまくいくでしょう:

#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;

ただし、shプロセスは、インタープリターがそのジョブを完了するまで存続するため、おそらく最適ではありません。フィードバックや提案をいただければ幸いです。

41
Rastapopoulos

Linuxカーネルは、Shebang行の最初の「Word」に続くすべてを単一の引数として処理する であるため、少なくともLinuxをサポートする必要がある場合は、一般的な解決策はありません。

NixOSの制約が何かはわかりませんが、通常、Shebangは次のように記述します

#!/bin/bash --posix

または、可能な場合は スクリプトでオプションを設定

set -o posix

または、適切なシェルを呼び出してスクリプトを再起動させることもできます。

#!/bin/sh -

if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

shift

# Processing continues

このアプローチは、最初の数行(シェルによって解釈される)がターゲット言語によって無視される方法を見つける限り、他の言語に一般化できます。

GNU coreutilsenvはバージョン8.30以降の回避策を提供します。詳細は node ’ s answer を参照してください。 (これは、Debian 10以降、RHEL 8以降、Ubuntu 19.04以降などで利用できます。)

29
Stephen Kitt

正確に移植可能ではありませんが、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
26
unode

POSIX標準は_#!_の記述に関して非常に簡潔です:

システムインターフェイスのexec()ファミリのドキュメントの根拠のセクション から:

一部の歴史的な実装がシェルスクリプトを処理する別の方法は、ファイルの最初の2バイトを文字列_#!_として認識し、ファイルの最初の行の残りを実行するコマンドインタープリターの名前として使用することです。

シェル紹介セクション から:

シェルは、ファイル(shを参照)、_-c_オプション、またはシステムインターフェイスで定義されているsystem()およびpopen()関数から入力を読み取りますPOSIX.1-2008のボリューム。 シェルコマンドのファイルの最初の行が文字_#!_で始まる場合、結果は指定されていません

これは基本的に、任意の実装(使用しているUnix)が自由にShebang行の解析の詳細を実行できることを意味します。

MacOS(ATMをテストできない)のような一部のUnicesは、Shebang行でインタープリターに与えられた引数を個別の引数に分割しますが、Linuxおよび他のほとんどのUnicesは、インタープリターに単一のオプションとして引数を提供します。

したがって、複数の引数をとることができるシェバン行に依存することは賢明ではありません。

WikipediaのShebang記事の移植性セクション も参照してください。


任意のユーティリティまたは言語に一般化できる1つの簡単な解決策は、適切なコマンドライン引数を使用して実際のスクリプトを実行するラッパースクリプトを作成することです。

_#!/bin/sh
exec /bin/bash --posix /some/path/realscript "$@"
_

個人的には、自分自身を再実行させようとは思わない(= /// =)自分自身少し壊れやすいように感じます。

10
Kusalananda

シバンについては、次のように execve(2) のマニュアルページで説明しています。

#! interpreter [optional-arg]

この構文では、2つのスペースを使用できます。

  1. インタプリタパスの前の1つのスペース。ただし、このスペースはオプションです。
  2. インタプリタパスとそのオプションの引数を区切る1つのスペース。

オプションの引数について話すとき、私は複数を使用しなかったことに注意してください。また、上記の構文は[optional-arg ...]最大1つの引数を指定できますとして使用していません。

シェルスクリプトに関しては、スクリプトの最初の方でset組み込みコマンドを使用できます。これにより、インタープリターパラメーターを設定でき、コマンドライン引数を使用した場合と同じ結果が得られます。

あなたの場合:

set -o posix

Bashプロンプトから、help setの出力を確認して、使用可能なすべてのオプションを取得します。

7
WhiteWinterWolf

Linuxでは、Shebangはそれほど柔軟ではありません。複数の回答( Stephen Kittの回答 および JörgW Mittag's )によると、Shebang行で複数の引数を渡すための指定された方法はありません。

それがだれにも役立つかどうかはわかりませんが、不足している機能を実装する短いスクリプトを作成しました。 https://Gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa を参照してください。

埋め込まれた回避策を書くことも可能です。怒鳴る、私は同じテストスクリプトに適用された4つのlanguage-agnostic回避策を提示し、それぞれの結果を印刷します。スクリプトは実行可能で、_/tmp/Shebang_にあると思います。


スクリプトをプロセス置換内のbashヒアドキュメントにラップする

私の知る限り、これは最も信頼できる言語に依存しない方法です。引数を渡し、標準入力を保持します。欠点は、インタープリターが読み取るファイルの(実際の)場所を知らないことです。

_#!/bin/bash
exec python3 -O <(cat << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv
try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER
) "$@"
_

_echo -e 'aa\nbb' | /tmp/Shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'_を呼び出すと、以下が出力されます。

_PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /dev/fd/62
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: False
PYTHON_SCRIPT_END
_

プロセスの置換により特別なファイルが生成されることに注意してください。これは、すべての実行可能ファイルに適しているとは限りません。たとえば、_#!/usr/bin/less_は不平を言います:/dev/fd/63 is not a regular file (use -f to see it)

ヒアドキュメントの内部プロセスをダッシュ​​で置き換えることが可能かどうかはわかりません。


簡単なヒアドキュメントにスクリプトをラップする

短くて簡単ですが、スクリプトからstdinにアクセスできなくなり、インタプリタがstdinからスクリプトを読み取って実行できるようになる必要があります。

_#!/bin/sh
exec python3 - "$@" << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER
_

_echo -e 'aa\nbb' | /tmp/Shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'_を呼び出すと、以下が出力されます。

_PYTHON_SCRIPT_BEGINNING
input() caused EOFError
argv[0]   :: -
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: True
PYTHON_SCRIPT_END
_

Awk system()呼び出しを使用しますが、引数はありません

実行されたファイルの名前を正しく渡しますが、スクリプトは指定された引数を受け取りません。 awkが私が知っている唯一の言語であり、そのインタープリターがデフォルトでLinuxにインストールされており、デフォルトでコマンドラインからその指示を読み取ることに注意してください。

_#!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
_

_echo -e 'aa\nbb' | /tmp/Shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'_を呼び出すと、以下が出力されます。

_PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/Shebang
argv[1:]  :: []
__debug__ :: False
PYTHON_SCRIPT_END
_

引数にスペースが含まれていない場合は、awk 4.1+ system()呼び出しを使用します

すばらしいですが、スペースを含む引数でスクリプトが呼び出されないことが確実な場合に限ります。ご覧のとおり、スペースがエスケープされていない限り、スペースを含む引数は分割されます。

_#!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
_

_echo -e 'aa\nbb' | /tmp/Shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'_を呼び出すと、以下が出力されます。

_PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/Shebang
argv[1:]  :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \\escapes\\']
__debug__ :: False
PYTHON_SCRIPT_END
_

4.1より前のawkバージョンでは、forループ内で文字列連結を使用する必要があります。関数の例を参照してください https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html =。

3
loxaxs

スクリプトを単一の引数として受け入れる実行可能ファイルを探しているとき、私はかなり愚かな回避策を見つけました。

#!/usr/bin/awk BEGIN{system("bash --posix "ARGV[1])}
2
till

シェル以外のものに依存せず、扱いを機能するLD_LIBRARY_PATH(Shebang)行でpythonを指定して#!を使用するトリック:

#!/bin/sh
'''' 2>/dev/null; exec /usr/bin/env LD_LIBRARY_PATH=. python -x "$0" "$@" #'''

__doc__ = 'A great module docstring'

このページの他の場所で説明されているように、shのような一部のシェルは、標準入力でスクリプトを実行できます。

shで指定したスクリプトは、shによって''''(空の文字列)に簡略化されたコマンド''を実行しようとしますが、もちろん次のように実行できません。 ''コマンドがないため、通常は標準エラー記述子にline 2: command not foundを出力しますが、このメッセージは2>/dev/nullを使用して最も近いブラックホールにリダイレクトします。 shに表示させるユーザー。

次に、目的のコマンドexecに進みます。これにより、現在のシェルプロセスが次のように置き換えられます。この場合、/usr/bin/env pythonが適切なパラメーターに置き換えられます。

  • "$0"を使用してpythonスクリプトを開いて解釈する必要があることを認識し、sys.argv[0]を設定します。
  • "$@"は、Pythonのsys.argv[1:]をスクリプトコマンドラインで渡された引数に設定します。

また、ハッキングの唯一のポイントであるLD_LIBRARY_PATH環境変数を設定するようenvに要求します。

シェルコマンドは#で始まるコメントで終了するため、シェルは末尾の三重引用符'''を無視します。

shは、pythonインタープリターの新しい新しいインスタンスに置き換えられます。これは、最初の引数として指定されたpythonソーススクリプトを開いて読み取ります( "$0")。

-x引数により、Pythonはファイルを開き、ソースの1行目をスキップします。 注:-xなしでも機能します。Pythonの場合、Shebangは単なるコメントだからです。

次に、Pythonは2行目を現在のモジュールファイルのdocstringとして解釈するため、有効なモジュールdocstringが必要な場合は、例のようにpythonプログラムの最初の__doc__を設定するだけです。上記。

2
Eric