FreeBSDシバンエラー

Aug 22 2020

#!/bin/sh -eufo pipefailスクリプトにシバンを入れたいと思います。しかし、奇妙なことがいくつかあります。

  1. スクリプトはFreeBSDでそのシバンで失敗しますが、MacOSで実行すると失敗します
  2. FreeBSdでは、コマンドラインから直接実行した場合も同じシバンが機能します(また/bin/sh)。
>>> sh -eufo pipefail -c 'echo hi'  # this works
hi

>>> cat <<EOF > script                                                                                      
#!/bin/sh -eufo pipefail
echo hi
EOF

>>> chmod +x ./script 
>>> ./script  # this doesn't work on FreeBSD but works on MacOS
Illegal option -o ./script

>>> cat ./script 
#!/bin/sh -eufo pipefail
echo hi

>>> uname -a
FreeBS 11.3-RELEASE-p7

回答

1 JdeBP Aug 22 2020 at 20:11

MacOSは、2005年以前の古いFreeBSDの動作を引き続き保持しています。2005年に、に#!渡された実行可能ファイルの開始時にFreeBSDカーネルが処理する方法に大きな変更があり、execve()他のオペレーティングシステムカーネルとより一致するようになりました。 LinuxとNetBSDカーネルを含みます。

NetBSDカーネルソースコードの解説は、これをユニバーサルとして表現しようとしています。

*シェル引数を収集します。シェル名の後のすべて
 * 1つの引数として渡されます。それは正しい(歴史的)
 *動作。
kern/exec-script.c。NetBSD。189行目以降。

実際にはそうではありません。Sven Mascheckは約10年前にいくつかのテストを行い、4つの基本的な動作があります。AT&TUnix System 5は、4.2BSDと同じくらい「正しい履歴」動作であると主張しています。

  • 文字を無視します(4.2BSDおよびAT&T Unix System 5より前)。
  • 文字列全体を単一の引数で渡します(2005年以降は4.2BSD、NetBSD、Linux、およびFreeBSD)。
  • 文字列を空白で分割し、複数の引数として渡します(2005より前のFreeBSDおよびMacOS)。
  • 文字列を空白で分割し、最初の引数だけを渡します(AT&T Unix System 5およびSolaris)

この回答に関連するオペレーティングシステムのみを括弧内に含めました。M. Mascheckは、FreeBSD Problem Report16393の議論でAhmonDancyが行ったように、さらに多くのことをチェックしました。完全なリストについては、さらに読むことを参照してください。

どのような2005年にはFreeBSDで頭に物事をもたらしたことは皮肉なことに、FreeBSDはなかった、ということでした、非常にそのような単純なよう。Perlに関する人気のある本に書かれていることを実際に機能させることを目的とした変更が導入されました。コメント文字の後に引数がスキップされました。本は次のようなことを推奨していました:

#!/ bin / sh-#-*-perl-*-
—ラリー・ウォール、トム・クリスチャンセン、ジョン・オルワント(2000)。Perlのプログラミング:第3版。オライリーメディア。ISBN 9780596000271.p。488。

2000年のPR16393は、カーネルに実行可能なPerlスクリプトを処理させる方法であり、LarryWallが同様に機能すると言っていた方法で記述されていました。しかし、それは他のものを壊し、完全には機能しませんでした。

これについては前後にいくつかありました。最後に、2005年に、Larry Wall et al。のアイデアを機能させるメカニズムがカーネルから移動されました。カーネルは、Linux、NetBSD、および4.2BSD(SolarisおよびAT&T Unix System 5ではなく)と互換性があるように作成され、の責任sh

したがって、2005年以降の動作では、シェルは3つの引数を取得し、2番目の引数は#!行の末尾全体であり、スクリプトを直接呼び出すことexecve()は、実質的に次の呼び出しと同じです。

sh'-eufo pipefail './script

Almquistシェル(shFreeBSD上にあるもの)がそれ./scriptがオプションのオプション引数であると考えている理由-o、およびその pipefail部分を背後に収集されたさらなる1文字のオプションとして扱っている理由はかなり明白です-(まだありません)まだ処理中です)。

またset -o pipefail、次のように指摘されているように、スクリプトの最初のコマンドとして持つことも明らかな代替手段です。https://unix.stackexchange.com/a/533418/5132以下のためのボーンアゲインシェル。これは2019年にFreeBSDAlmquistシェルにのみ追加されたため、ごく最近のバージョンのFreeBSDでのみ使用できます。(2020年の時点で、Debian Almquistシェルにはまだ追加されていません。)

参考文献

schily Aug 23 2020 at 13:53

#!単純なもの以上のものを使用しているため、使用法は移植性がありません。

#!/bin/sh

または、次のように一般的にサポートされている単一の引数。

#!/bin/sh -oneflag

本当の問題は、移植性のないオプションを使用していることです-o pipefail

これはからのオプションでksh93ありbash、他のシェルではサポートされていません。

バックグラウンド:

  • MacOSの上、/bin/shされてbashいるが(例えばにエスケープシーケンスを作るために、特定の方法でコンパイルされたecho、それはPOSIX準拠させるために、デフォルトで作業)。bashはpipefail(上記を参照)をサポートしているため、これはMacOSで機能します。

  • FreeBSDでは、/bin/shあるashことはサポートしていませんpipefail

2つの方法があります。

  • 使用禁止 set -o pipefail

  • 2年間待ってから、もう一度やり直してください。10か月前にこのオプションを次のPOSIX標準(Issue-8)に追加することを決定したため、これはおそらく機能します。を参照してください。https://www.austingroupbugs.net/view.php?id=789そして、次のPOSIX標準が約で公開されるので。一年間、FreeBSDはのサポートを追加することを大きなチャンスがあるset -o pipefailashすぐには。