Shebangで `/usr/bin/env sed -f`を使用する方法は?

Shebangで `/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に関連していると思います。しかし、この問題をどのように解決するのですか?

答え1

#!1行に複数のパラメータを含めることはできません。。これは、フルパスと1つの引数(たとえば、#!/bin/sed -fまたは#!/usr/bin/sed -f)しかないか、#!/usr/bin/envインタプリタの引数がないことを意味します。

移植可能なスクリプトを取得する回避策は、シェルラッパーを使用#!/bin/shしてsedスクリプトをコマンドライン引数として渡すことです。これはPOSIX承認ではありませんが(移植性のために複数の命令スクリプトは-e各命令に対して別々のパラメータを作成する必要があります)、多くの実装で機能します。

#!/bin/sh
exec sed '
s/a/b/
' "$@"

長いスクリプトの場合は、heredocを使用する方が便利です。 Heredocの1つの利点は、内部単一引用符(存在する場合)を引用する必要がないことです。 1つの主な欠点は、スクリプトが標準入力を介して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はスキップするのが面倒です)。

答え2

GNU coreutilsで始めるv8.30、あなたはできます:

#!/usr/bin/env -S sed -f

この機能は最近追加されました (2018-04-20)犯罪またはオプションがenv.c追加されたGNU coreutilsパッケージに。-S--split-string

envマニュアルページから:

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 マニュアル

verbose出力オプションも使用して、引数文字列がどのように-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’

メモ:これは、GNUの特殊機能である/usr/bin/envshebangを使用している場合にのみ機能します。--split-stringenv

答え3

オペレーティングシステムによっては、shebang(#!)の互換性のないさまざまな実装があります。一部は完全な引数リストを作成し、一部はコマンドパスを保持し、残りのすべての引数を単一の引数として渡し、一部はすべての引数を無視してコマンドパスのみを渡し、最後に一部は文字列全体をコマンドに渡します。単一引数あなたは後者の状況にあるようです。

答え4

この答えはエレガントなソリューションへのパスを提供します。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" "$@"

関連情報