
2026/06/29 5:39
POSIX はシェルではありません
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
真のシェルの移植性を達成するには、"'POSIX 準拠'というラベルに頼ることだけで十分ではない」ことが必要です。Bash や Dash など主要な実装は POSIX が一部の振る舞い(例えば
echo のバックスラッシュエスケープなど)を実装定義とすることを認めているにもかかわらず、コマンドを異なって解釈するためです。例えば、\n の扱いがシェル間で異なるほか、[[ ]] といった Bash 固有の構文は、Ubuntu や Alpine Linux などに存在する Dash などのミニマルな実装に対する /bin/sh の設定があるシステムでは、沈黙した失敗を引き起こすことがよくあります。これらの不一致は、単なるバグではなく、異なる標準バージョンを経て蓄積された歴史的判断に起因しており、自然言語の方言的な変異と似ています。コミュニティは頻繁に、準拠とは CI テストを通過することや Bash のオプション(例: -e)を使用することと混同しており、それは厳格な移植性を保証しません。shell-docs などのツールを用いて 14 つのシェルにわたる機能を検証した実験が示す通り、変数の作用域のような一貫性のある要素であっても分岐することがあります。したがって、開発者は準拠を前提とする姿勢から、Ubuntu、macOS、Alpine、企業向け Linux など特定の /bin/sh バリエーションを含む多様な現実環境でスクリプトを能動的に検証する姿勢へと移行し、構文が同等の結果を与えない場合の広範な本番環境での失敗を防ぐ必要があります。
Text to translate:
To achieve true shell portability requires more than relying on "POSIX compliant" labels, as major implementations like Bash and Dash interpret commands differently despite POSIX leaving certain behaviors (such as
echo backslash escapes) implementation-defined. For example, handling of \n varies between shells, and Bash-exclusive constructs like [[ ]] often cause silent failures on systems where /bin/sh points to minimal implementations like Dash found in Ubuntu or Alpine Linux. These discrepancies stem from accumulated historical decisions across different standard versions rather than simple bugs, similar to dialectical variations in natural languages. The community frequently conflates compliance with passing CI tests or using Bash options like -e, which does not guarantee strict portability. As shown by experiments with tools like shell-docs validating features across 14 shells, even seemingly consistent elements like variable scoping can diverge. Consequently, developers must shift from assuming compliance to actively verifying scripts across diverse real-world environments—such as the specific /bin/sh variants found in Ubuntu, macOS, Alpine, and enterprise Linux—to prevent widespread production failures where syntax does not yield identical results.本文
Shell スクリプティングにおける「可搬性」と POSIX 真実
はじめに:POSIX は仕様でありプログラムではない
「可搬性の確保のために POSIX シェルで書け」と言われたとき、その意図は本気で実行したいことです。ただし、以下の事実を理解する必要があります。
- POSIX は仕様です: プログラムやコンパイラではありません。
- 多様なシェルが存在する: バシュ(bash)、dash、ash、ksh など、十数種類の実装が存在します。これらはすべて POSIX を独自に解釈・実装しています。
- 差異が生まれる理由:
- 仕様の欠落部分
- 拡張機能の違い
- 歴史的事故
シンプルなコードでも結果が異なる
以下のように、一行だけの単純なコマンドでもシェル依存で結果が異なります。
#!/bin/sh echo "C:\new"
シェルごとの出力比較
- bash / ksh など: 文字列をそのまま返します。
C:\new - dash (Debian/Ubuntu/Alpine の
):/bin/sh
を改行と解釈し、出力を変化させます。\nC: ew
重要: POSIX 仕様の明記によれば、
コマンドのバックスラッシュエスケープ挙動は実装依存です。echoを使用することを推奨していますが、「POSIX 準拠の echo」という振る舞いをスクリプト内で確実にするのは不可能です。printf
ディストリビューションごとの /bin/sh
の実態
/bin/sh主要な OS で標準として採用されているシェルのバージョンは全く異なります。
docker run --rm alganet/shell-versions:all -c 'echo "C:\new"'
各環境における
/bin/sh の現状は以下の通りです:
- Ubuntu 24.04: dash 0.5.12
- macOS: bash 3.2.57 (15 年変更なし;GPL v3 のため bash 4.x は含まれない)
- Alpine: busybox ash
- 一部のエンタープライズ Linux: ksh93
これらは互いに異なるプログラムであり、年代もバラバラです。
「検証」の実態:方言の課題
自然言語にはブラジルポルトガル語とヨーロッパポルトガル語のように方言が存在するがごとく、シェルスクリプティングでも「どこでも同じ結果」とは限りません。
- bash は「シェル」ではない: 特定の振る舞いを持つ実装です。
- 矛盾する存在: いくつかの機能は POSIX 準拠と解釈される実装(dash など)とは衝突します。
- コミュニティの曖昧な表現:
- 「sh スクリプト」と言った場合 → 実際には「bash で
オプションを付与した状態」を指すことが多い。-e -u -o pipefail - 「POSIX 準拠」と言うと → 「CI で動作する」という意味になりがち。
- 「可搬性」と言うと → 「試した二台の機械で動く」という狭い範囲を指す。
- 「sh スクリプト」と言った場合 → 実際には「bash で
クロス・シェルリファレンスの事実
独立した実装が行ってきた蓄積の決断により、機能の動作には差異が存在します。これらはバグではなく、仕様上の違いです(例:1988 年、2001 年、2017 年の不同時点での仕様)。
主な機能における挙動の違い:
: ポジショナルパラメータの数値。どのシェルでも問題なく動作。$#
: POSIX に含まれていないが全シェルで存在する。スコープの規則は実装ごとに異なる。local
: 算術展開は全シェルでサポートされるが、ゼロ除算の結果は実装により異なる。$(( ))
: POSIX に含まれない(dash では存在しない)。[[ ]]
でこの構文を使うと、dash が#!/bin/sh
となっているシステム(Debian/Ubuntu/Alpine)ではエラーになる。/bin/sh
「可搬性」の実像とは?
「可搬性」とは、実際に動作するシェルの範囲内で検証されたことを意味します。単に
#!/bin/bash と書くのは誠実ですが、#!/bin/sh を書いて POSIX 準拠と約束するには、検証が必須です。
shell-docs の検証プロセス
文書化された機能の動作を確認するために、14 種類のシェルすべてに対して以下の手順を踏みます。
- 例を書く: コードを作成する。
- 全実装実行: すべてのシェルのバージョンで実行し、標準出力と終了コードを記録する。
- 差異チェック: 結果の不一致を検証する。
新しいシェルバージョンがリリースされたら、再度実行すればよいだけです。複雑な作業ではなく、地道な検証こそが重要です。
結論:検証は習慣化するべき
「POSIX sh で動作する」という表現が意味するのは、「私がテストしたすべてのシェルで動作し、以下に結果を示します」ということです。
- 単なる主張: 「bash で実行してみて問題なかった」
- 真の可搬性: 「検証済みの実装でのみ動作保証される」
POSIX はシェルそのものではなく、シェルの振る舞いについての約束です。検証こそが、どの実装がこの約束を守っているかを明らかにする唯一の方法です。