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


25

/usr/bin/env sed -fターミナルでの入力は機能します。

しかし、それをシバンとして使用する場合、

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

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

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

-fに関連していると思う。しかし、これを解決する方法は?


回答:


33

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を使用する方が便利な場合があります。ヒアドキュメントの利点は、内部に単一引用符がある場合でも引用符で囲む必要がないことです。主な欠点は、スクリプトが標準入力でsedに送られることで、2つの厄介な結果が生じます。sedの一部のバージョンで-f /dev/stdin-f -、の代わりにが必要です。これは、移植性の問題です。さらに悪いことに、標準入力はスクリプトであり、データではないため、スクリプトはフィルターとして機能できません。

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF

heredocの欠点は、の有用な使用によって改善できますcat。これにより、スクリプト全体がコマンドラインに再び配置されるため、POSIX準拠ではありませんが、実際にはほとんど移植可能です。

#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF

別の回避策は、shとsedの両方で解析できるスクリプトを作成することです。これは移植性があり、適度に効率的で、少しlittleいです。

#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/

説明:

  • shの下で、という関数を定義しますb。関数が構文的に整形式である限り、内容は重要ではありません(特に、空の関数を作成することはできません)。次に、true(常に)の場合sed、スクリプトを実行します。
  • Under sed:()ラベルへの分岐、そして整形式入力。次に、iコマンドは常にスキップされるため、効果はありません。最後に、()ラベルの後にスクリプトの有用な部分が続きます。
  • GNU sed、BusyBox、およびOpenBSDでテスト済み。(GNU sedでもっと簡単なもので逃げることができますが、OpenBSD sedはスキップする部分にこだわりがあります。)

1
「または#!/usr/bin/env引数なし。」あまりうまく言い表されていません。おそらく「または#!/usr/bin/env sedsedに対する議論はありません」。
cjm

「ほんの少しい」と二言語スクリプトの+1 :)
retracile

6

OSによって、シバン(#!)のさまざまな非互換の実装があります。完全な引数リストを作成しているもの、コマンドパスを保存して残りのすべての引数を1つにまとめているもの、すべての引数を無視してコマンドパスのみを渡すもの、最後に文字列全体を1つとして渡すものコマンド。後者の場合のようです。


2

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


2

GNU coreutils v8.30以降、次のことができます。

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

この機能は、最近(2018年4月20日)に添加し、コミットenv.c追加GNU coreutilsのパッケージ、中-Sまたは--split-stringオプションを選択します。

envmanページから:

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’

注:これはのみ使用shebangsに適用される/usr/bin/envとして、 --split-stringGNUの特徴であるenv、具体的。


1

この回答は、エレガントなソリューションへのパスを提供します: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のソリューションが好きですが、改善することができます(コメントはフォーマット済みのテキストを受け入れないため、これを別の回答に入れる)。これは、LinuxまたはBashのすべてのバージョンで機能するわけではありませんが、Ubuntu 17.10では次のように削減できます。

#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"

を削除しevalreadコマンドを簡略化できますが、ヒアドキュメント内のコメントを削除する必要があります。

また、sedについて知りたいと思っていたが、尋ねるのが怖かったすべてのために、http: //www.grymoire.com/unix/sed.htmlに非常に有用なチュートリアルがあります。これは、/usr/bin/envsedへのパスを失い、ハードコーディングすることを犠牲にして、さらに優れたソリューションを示唆しています。

#!/bin/sed -f
s/a/b/
s/c/d/

これには、複数のs///置換をサポートするという追加の利点があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.