テクノロジー · 実用ガイド

sed の「unterminated s command」は、エスケープを足す前に境界を証明する

この診断は、sed に届いた置換プログラムに有効な終端境界がないことを示すだけです。実際の引数とバイトを取得し、4種類に分類してから、インプレース編集なしで修正を検証します。

シェルソースから実際の引数ベクトルを経て sed パーサーへ届く流れの図
概念図:エスケープ変更前に sed に届いた正確な引数とバイトを確認します。 Watch at Ease

「unterminated s command」はパーサーの報告であり、完全な原因診断ではありません。sed に届いた置換プログラムでは、期待位置の終端区切りが失われています。元の構文不足、データ内の区切り、展開値の改行や CR、または前段パーサーによる引用符やバックスラッシュの消費が考えられます。

プロセス境界から確認します。エスケープを変更する前に、sed に渡された正確な引数とバイトを取得します。壊れた境界が見えたら所有する層だけを直し、小さなフィクスチャで -i を使わず出力を比較し、差分が完全に一致してからインプレース編集を追加します。

1. sed が実際に受け取ったものを取得する

最小入力で再現し、まず -i を外します。シェルトレースは手掛かりにすぎません。対応シェルでは引数を1行ずつ安全に表示し、od や xxd で端末表示に隠れる区切り、バックスラッシュ、CR、改行を確認します。

対話シェルまたはスクリプト、パラメータ展開、コマンド置換、YAML や CI、SSH やコンテナのラッパー、最後に sed という順で所有者を記録します。eval は新しいパーサーを増やして証拠を変えるため使いません。

2. 壊れた境界を分類する

最終引数に3番目の区切りがなければ構文を直します。データに区切りがあれば別の区切りを選ぶか、そのフィールドだけをエスケープします。CR や改行があれば、多行データがこのコマンドに属するかを先に判断します。

ローカルでは正しく CI、YAML、SSH、コマンド置換で壊れる場合、各受け渡しのバイトを比較します。最初に変わる境界が修正箇所であり、後段でバックスラッシュを増やすのは原因を隠します。

3. パターンと置換を別々に処理する

正規表現フィールドと置換フィールドは別の言語です。正規表現メタ文字はパターン側の判断です。置換では未エスケープの & が一致文字列を挿入し、バックスラッシュが置換エスケープを開始します。シェル引用の後に、そのフィールドに必要な最小限だけを処理します。

データが複雑なら、フィールド専用エンコーダーか管理されたテンプレートから sed スクリプトファイルを生成します。構造化パーサーや言語ライブラリで境界を明示できるなら、不信な文字列をシェルや sed 構文へ連結しません。

4. 正常値1つではなく故障マトリクスを試す

空値、空白、選択区切り、&、バックスラッシュ、CR、改行、不一致値を小さなフィクスチャで試します。stdout を正確な期待ファイルと比較し、エラーが消えただけの誤変換を合格にしません。

以前失敗した実際のラッパー経由でも繰り返します。端末での成功は YAML、SSH、コンテナ入口、ビルド変数を証明しません。取得引数と期待出力を回帰テストに残します。

5. 差分一致後にだけインプレース編集を追加する

合格した式をまずコピーに実行します。GNU、BSD、BusyBox sed では -i とバックアップ接尾辞が異なるため実装を確認し、重要ファイルにはバックアップかアトミック置換を用意します。

実際のラッパーが審査済みバイトを出し、フィクスチャ差分が一致し、端のデータがリテラルのままで、コピーが一度だけ変われば完了です。不明な所有者やバイトが残るなら stdout で停止し証拠を保存します。

未終端の sed 置換を4種類に分類して修正するマトリクス
概念イラスト:所有する故障クラスを直し、非インプレースの受け入れ試験を通します。 Watch at Ease