web-dev-qa-db-ja.com

シバンで `/ usr / bin / env sed -f`を使用する方法?

入力/usr/bin/env sed -fターミナルで動作します。

でもシバンとして使うなら

#!/usr/bin/env sed -f 
s/a/b/

スクリプトは実行に失敗します:

/usr/bin/env: sed -f: No such file or directory

私はそれが-fに関連していると信じています。しかし、これを解決するにはどうすればよいですか?

26
Cheng

移植可能に、#!行に複数の引数を指定することはできません 。つまり、フルパスと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/

説明:

  • Shの下:bという関数を定義します。関数が構文的に整形式である限り、内容は重要ではありません(特に、空の関数を持つことはできません)。次に、trueの場合(つまり、常に)、スクリプトでsedを実行します。
  • Sed:の下で()ラベルに分岐し、適切な形式の入力をいくつか入力します。次に、iコマンドを実行します。このコマンドは常にスキップされるため、効果はありません。最後に、()ラベルとそれに続くスクリプトの有用な部分。
  • GNU sed、BusyBox、およびOpenBSDの下でテストされています。(GNU sedでもっと簡単な方法で回避できますが、OpenBSD sedはスキップする部分に注意を払っています。 )

OSに応じて、Shebang(#!)には互換性のないさまざまな実装があります。完全な引数リストを作成しているもの、コマンドパスを保持して残りのすべての引数を1つとして配置しているもの、すべての引数を無視してコマンドパスのみを渡しているもの、最後に文字列全体を1つとして渡しているものがあります。コマンド。あなたは後者のケースにいるようです。

6
jlliagre

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を使用するシバンにのみ適用されます。

3
Amin Mesbah

envは "sed -f"という名前のファイルを見つけようとしています。 Shebang行として「#!/ usr/bin/sed -f」を試すことができます。

2
anilmwr

私はウォーカーの解決策が好きですが、改善することができます(コメントが事前にフォーマットされたテキストを受け入れないため、これを別の回答に入れてください)。これは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///置換をサポートするという追加の利点があります。

1
Huw Walters

この回答は、エレガントなソリューションへのパスを提供します: https://stackoverflow.com/a/1655389/642372

  1. readシェル変数へのヒアドキュメント。
  2. その変数を位置引数としてsedに渡します。

サンプル:

#!/usr/bin/env bash

read -rd '' SED_SCRIPT <<EOD      
# Your sed script goes here.
s/a/b/
EOD

exec sed "$SED_SCRIPT" "$@"
1
Walker Hale IV