入力/usr/bin/env sed -f
ターミナルで動作します。
でもシバンとして使うなら
#!/usr/bin/env sed -f
s/a/b/
スクリプトは実行に失敗します:
/usr/bin/env: sed -f: No such file or directory
私はそれが-fに関連していると信じています。しかし、これを解決するにはどうすればよいですか?
移植可能に、#!
行に複数の引数を指定することはできません 。つまり、フルパスと1つの引数(例:#!/bin/sed -f
または#!/usr/bin/sed -f
)のみ、または#!/usr/bin/env
とインタープリターへの引数なしを意味します。
移植可能なスクリプトを取得するための回避策は、#!/bin/sh
とシェルラッパーを使用して、sedスクリプトをコマンドライン引数として渡すことです。これはPOSIXによって認可されていません(複数の命令スクリプトは、移植性のために各命令に対して個別の-e
引数を使用して記述する必要があります)が、多くの実装で機能することに注意してください。
#!/bin/sh
exec sed '
s/a/b/
' "$@"
長いスクリプトの場合は、ヒアドキュメントを使用する方が便利な場合があります。ヒアドキュメントの利点は、内部の単一引用符があれば引用符で囲む必要がないことです。大きな欠点は、スクリプトが標準入力でsedに送られることです。2つの厄介な結果があります。 sedの一部のバージョンでは、移植性の問題である-f /dev/stdin
ではなく-f -
が必要です。さらに悪いことに、標準入力はスクリプトであり、データではないため、スクリプトはフィルターとして機能できません。
#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF
ヒアドキュメントの欠点は、cat
を便利に使用することで改善できます。これにより、スクリプト全体が再びコマンドラインに配置されるため、POSIXに準拠していませんが、実際にはほとんど移植可能です。
#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF
別の回避策は、shとsedの両方で解析できるスクリプトを作成することです。これは移植可能で、かなり効率的ですが、少し醜いです。
#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/
説明:
b
という関数を定義します。関数が構文的に整形式である限り、内容は重要ではありません(特に、空の関数を持つことはできません)。次に、trueの場合(つまり、常に)、スクリプトでsed
を実行します。()
ラベルに分岐し、適切な形式の入力をいくつか入力します。次に、i
コマンドを実行します。このコマンドは常にスキップされるため、効果はありません。最後に、()
ラベルとそれに続くスクリプトの有用な部分。OSに応じて、Shebang(#!)には互換性のないさまざまな実装があります。完全な引数リストを作成しているもの、コマンドパスを保持して残りのすべての引数を1つとして配置しているもの、すべての引数を無視してコマンドパスのみを渡しているもの、最後に文字列全体を1つとして渡しているものがあります。コマンド。あなたは後者のケースにいるようです。
GNU coreutils v8. 以降、次のことができます。
#!/usr/bin/env -S sed -f
この機能は、最近の(2018-04-20) commit でGNU coreutilsパッケージのenv.c
に追加され、-S
または--split-string
オプションが追加されました。
env
manページから:
OPTIONS
-S/--split-string usage in scripts
The -S option allows specifing multiple parameters in a script.
Running a script named 1.pl containing the following first line:
#!/usr/bin/env -S Perl -w -T
Will execute Perl -w -T 1.pl .
Without the '-S' parameter the script will likely fail with:
/usr/bin/env: 'Perl -w -T': No such file or directory
See the full documentation for more details.
その他の例は GNU coreutilsマニュアル にあります。
詳細出力に-v
オプションも使用すると、env
が引数の文字列を分割する方法を正確に確認できます。
my_sed_script.sed
内:
#!/usr/bin/env -vS sed -f
s/a/b/
実行:
$ ./my_sed_script.sed
split -S: ‘sed -f’
into: ‘sed’
& ‘-f’
executing: sed
arg[0]= ‘sed’
arg[1]= ‘-f’
arg[2]= ‘./my_sed_script.sed’
注:/usr/bin/env
はGNU env
の機能であるため、--split-string
を使用するシバンにのみ適用されます。
envは "sed -f"という名前のファイルを見つけようとしています。 Shebang行として「#!/ usr/bin/sed -f」を試すことができます。
私はウォーカーの解決策が好きですが、改善することができます(コメントが事前にフォーマットされたテキストを受け入れないため、これを別の回答に入れてください)。これはLinuxまたはBashのすべてのバージョンでは機能しない可能性がありますが、Ubuntu 17.10ではこれを次のように削減できます。
#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"
eval
を削除してread
コマンドを簡略化できますが、ヒアドキュメント内のコメントを削除する必要があります。
また、sedについて知りたかったが質問するのが怖かったすべてのものについては、 http://www.grymoire.com/unix/sed.html に非常に役立つチュートリアルがあります。これは、/usr/bin/env
が失われ、sedへのパスがハードコーディングされるという犠牲を払って、さらに優れたソリューションを提案します。
#!/bin/sed -f
s/a/b/
s/c/d/
これには、複数のs///
置換をサポートするという追加の利点があります。
この回答は、エレガントなソリューションへのパスを提供します: https://stackoverflow.com/a/1655389/642372
read
シェル変数へのヒアドキュメント。sed
に渡します。サンプル:
#!/usr/bin/env bash
read -rd '' SED_SCRIPT <<EOD
# Your sed script goes here.
s/a/b/
EOD
exec sed "$SED_SCRIPT" "$@"