追加読書

追加読書

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

  1. FreeBSDでは、このshebangはスクリプトを失敗しますが、MacOSで実行しても失敗しません。
  2. /bin/shFreeBSdでは、コマンドラインから直接実行すると同じshebangが機能します(また)。
>>> 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

#!MacOSはまだ2005年以前のFreeBSD動作を維持しています。 2005年に、execve()LinuxおよびNetBSDカーネルを含む他のオペレーティングシステムカーネルとの一貫性を高めるために、FreeBSDカーネルが渡された実行可能ファイルの先頭を処理する方法が大幅に変更されました。

NetBSDカーネルソースコードのコメントは、これを普遍的なものとして描写しようとします。

*シェルパラメータを収集します。シェル名の後ろのすべて
 *パラメータとして渡されました。これは正しいです(歴史的)。
 *アクション。
—— kern/exec-script.c。 WebBSD。行189 et seq。

そうではありません。 Sven Mascheckは約10年前にいくつかのテストを実行し、次の結果が出ました。4つデフォルトの動作に関して、AT&T Unix 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問題報告書16393の議論で、Ahmon Dancyが行ったように、より多くのことを確認しました。完全なリストについては追加資料をご覧ください。

皮肉なことに、2005年のFreeBSDの鍵は、FreeBSDがそうでなかったことです。かなりとても簡単です。 Perlについて人気のある本に書かれた内容が実際に機能するように設計された変更が導入されました。つまり、コメント文字の後に引数をスキップすることです。この本では、以下をお勧めします。

#!/bin/sh -- # -*- パール -*-
— ラリー・ウォール、トム・クリスティアンソン、ジョン・オールワント(2000)。Perlを使ったプログラミング:第3版。オライリーメディア。 ISBN 9780596000271。 488.

2000年のPR 16393は、Larry Wallが言った方法で作成された実行可能なPerlスクリプトをカーネルに処理させる方法でした。しかし、他の機能が中断され、完全には機能しません。

これについて前後に話がありました。最後に、2005年にLarry Wallなどのアイデアを実装したメカニズムがカーネルから移され、Linux、NetBSD、および4.2BSD(SolarisおよびAT&T Unix System 5ではない)と互換性があるように設計されています。そしてそれをLinuxの責任にしましたsh

したがって、2005年以降の動作は、シェルが3つの引数を取るということです。 2番目の引数は#!行の完全な尾です。スクリプトを直接呼び出すことは、execve()呼び出しとほぼ同じです。

sh '-eufoパイプライン失敗' ./script

shAlmquistシェル(FreeBSDで)がこれが./scriptこのオプションのオプションパラメータであると考え、その部分を後で収集された追加の文字オプションとして-o扱う理由は明らかです(このオプションは得られません)。まだ処理されていません)。pipefail-

もう一つの明確な選択肢は、set -o pipefail指摘したようにスクリプトの最初のコマンドです。https://unix.stackexchange.com/a/533418/5132~のためボン覇権シェル。これはFreeBSDにのみ追加されました。アルムキストただし、シェルは2019年に導入されたため、最新バージョンのFreeBSDでのみ利用可能です。 (これダーバン2020年現在、Almquistシェルはそれを追加していません。 )

追加読書

答え2

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

#!/bin/sh

または、次のように一般的にサポートされている単一パラメータです。

#!/bin/sh -oneflag

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

ksh93これはbash他のシェルではサポートされていないオプションです。

背景:

  • MacOSでは、POSIXと互換性のある特定の方法(デフォルトではエスケープシーケンスの動作など)でコンパイルされます/bin/sh。 bashのサポートにより、MacOSで動作します(上記を参照)。bashechopipefail

  • FreeBSDでは/bin/shこれはashサポートされていませんpipefail

2つの可能なアプローチがあります。

  • 使用しないでくださいset -o pipefail

  • 2年待ってからもう一度お試しください。 10か月前に、このオプションを次のPOSIX標準(問題8)に追加することにしたので、これはおそらく効果的です。https://www.austingroupbugs.net/view.php?id=789次のPOSIX規格は2019年ごろに発売される予定です。これから1年後、FreeBSDがまもなくset -o pipefailサポートを追加する可能性が高くなりますash

関連情報