POSIX sedは、アドレス範囲が既に削除された行から始まる `1d; 1,2d`に何を必要としますか?


11

この質問へのコメントで、さまざまなsed実装がかなり単純なプログラムで同意しない場合があり、私たち(または少なくとも私)は、仕様が実際にそれに対して必要とするものを決定できませんでした。

問題は、削除された行から始まる範囲の動作です:

1d;1,2d

そのコマンドに到達する前に範囲の開始が削除された場合でも、行2を削除する必要がありますか?私の当初の期待はBSD sedに沿って「いいえ」でしたが、GNU sedは「はい」と言っており、仕様テキストをチェックしても問題は完全には解決しません。

私の予想に一致するのは、(少なくとも)macOSとSolaris sed、およびBSD sedです。反対は(少なくとも)GNUとBusybox sedであり、多くの人々がここにいます。最初の2つはSUS認定で、他の2つはより普及している可能性があります。どの動作が正しいですか?


2アドレス範囲の仕様書には次のように記載されています。

sedのコマンドは、次のサイクルが終了しますかを開始するまでのユーティリティはその後、順番にそのアドレスがそのパターンスペースを選択するすべてのコマンドを適用しなければなりません。

そして

2つのアドレスを持つ編集コマンドは、最初のアドレスに一致する最初のパターンスペースから2番目のアドレスに一致する次のパターンスペースまでの包括的な範囲を選択します。[...]選択した範囲に続く最初の行から開始して、sedは最初のアドレスを再度探します。その後、プロセスが繰り返されます。

おそらく、ライン2がある 範囲内にかかわらず開始点が削除されたかどうかの、「第二に一致する次のパターン空間を介して第1のアドレスと一致する第1のパターン領域からの包含範囲」。一方、私は最初dが次のサイクルに移動し、範囲に開始する機会を与えないことを期待していました。UNIX™認定の実装は、私が期待したことを実行しますが、仕様で要求されていることを潜在的に実行しません。

いくつかの実例となる実験が続きますが、重要な問題は、削除された行で範囲が始まるときに何をすべき sedかです。


実験と例

問題の簡単なデモンストレーションはこれです。これは、行を削除するのではなく、余分なコピーを印刷します。

printf 'a\nb\n' | sed -e '1d;1,2p'

これは、提供するsed入力の2行、aおよびb。プログラムは2つのことを行います。

  1. で最初の行を削除し1dます。dコマンド意志

    パターンスペースを削除し、次のサイクルを開始します。そして

  2. 各行が受け取る自動印刷に加えて、1から2までの行の範囲を選択して明示的に印刷します。したがって、範囲に含まれる行は2回表示されます。

私の期待は、これが印刷されることでした

b

ただ、1,21行目で到達できないため(d次のサイクル/行に既にジャンプしているため)範囲aが適用されず、範囲の包含は決して開始されず、削除されます。SolarisおよびBSDの一般的なsed非POSIXと同様に、macOSおよびSolaris 10 の準拠するUnixがこの出力を生成します。sedsed

一方、GNU sedは印刷します

b
b

範囲解釈したこと示します。これはPOSIXモードでもそうでない場合でも発生します。Busyboxのsedは同じ動作をします(ただし、常に同じ動作とは限らないため、共有コードの結果ではないようです)。

さらなる実験

printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/c/p'
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/d/p'

削除された行から始まる範囲を次の行から始まるかのように扱うように見えます。/c/範囲を終了するために一致しないため、これは表示されます。を使用/b/して範囲を開始すると、と同じように動作しませ2


私が使用していた最初の実用的な例は

printf '%s\n' a b c d e | sed -e '1{/a/d;};1,//d'

/a/最初の行にある場合でも、最初の一致までのすべての行を削除する方法として(GNU sedが使用0,/a/dするもの-これはPOSIX互換の試みでした)。

代わりに、最初の行が一致する場合(または2番目の一致が/a/ない場合はファイル全体)の2番目の一致まで削除することをお勧めします。これはもっともらしいと思われます-ただし、GNU sedのみがそれを行います。macOS sedとSolarisのsed製品の両方

b
c
d
e

私は予想通りそのため、(; Busyboxのは、単にプリントをsedのGNUは、終端されていない範囲を削除するから、空の出力を生成sedをdし、eどんな間違っ明確ではありませんこれは、)。一般的に、私は彼らが認定適合テストに合格したことは彼らの行動が正しいことを意味すると仮定しますが、十分な人々は私が確信していないことを示唆しており、仕様のテキストは完全に説得力がなく、テストスイートはできません完全に包括的です。

明らかに、矛盾を考慮して今日そのコードを書くことは実際には移植性がありませんが、理論的にはどこかで何らかの意味で同等でなければなりません。これはバグだと思いますが、どの実装に対して報告するのかわかりません。私の現在の見解では、GNUおよびBusybox sedの動作は仕様と矛盾していますが、それについて誤解される可能性があります。

ここでPOSIXには何が必要ですか?


一時的な回避策として、一時ファイルに書き込み、POSIX edで処理し、sed完全にバイパスしますか?
D.ベンKnoble

回答:


9

これは、2012年3月にオースティングループのメーリングリストで提起されました。その最後のメッセージを次に示します(オースティングループのGeoff Clare(POSIXを管理する機関)、最初に問題を提起した人物)。ここでは、gmane NNTPインターフェースからコピーしました。

Date: Fri, 16 Mar 2012 17:09:42 +0000
From: Geoff Clare <gwc-7882/jkIBncuagvECLh61g@public.gmane.org>
To: austin-group-l-7882/jkIBncuagvECLh61g@public.gmane.org
Newsgroups: gmane.comp.standards.posix.austin.general
Subject: Re: Strange addressing issue in sed

Stephane Chazelas <stephane_chazelas-Qt13gs6zZMY@public.gmane.org> wrote, on 16 Mar 2012:
>
> 2012-03-16 15:44:35 +0000, Geoff Clare:
> > I've been alerted to an odd behaviour of sed on certified UNIX
> > systems that doesn't seem to match the requirements of the
> > standard.  It concerns an interaction between the 'n' command
> > and address matching.
> > 
> > According to the standard, this command:
> > 
> > printf 'A\nB\nC\nD\n' | sed '1,3s/A/B/;1,3n;1,3s/B/C/'
> > 
> > should produce the output:
> > 
> > B
> > C
> > C
> > D
> > 
> > GNU sed does produce this, but certified UNIX systems produce this:
> > 
> > B
> > B
> > C
> > D
> > 
> > However, if I change the 1,3s/B/C/ to 2,3s/B/C/ then they produce
> > the expected output (tested on Solaris and HP-UX).
> > 
> > Is this just an obscure bug from common ancestor code, or is there
> > some legitimate reason why this address change alters the behaviour?
> [...]
> 
> I suppose the idea is that for the second 1,3cmd, line "1" has
> not been seen, so the 1,3 range is not entered.

Ah yes, now it makes sense, and it looks like the standard does
require this slightly strange behaviour, given how the processing
of the "two addresses" case is specified:

    An editing command with two addresses shall select the inclusive
    range from the first pattern space that matches the first address
    through the next pattern space that matches the second.  (If the
    second address is a number less than or equal to the line number
    first selected, only one line shall be selected.) Starting at the
    first line following the selected range, sed shall look again for
    the first address. Thereafter, the process shall be repeated.

It's specified this way because the addresses can be BREs, but if
the same matching process is applied to the line numbers (even though
they can only match at most once), then the 1,3 range on that last
command is never entered.

-- 
Geoff Clare <g.clare-7882/jkIBncuagvECLh61g@public.gmane.org>
The Open Group, Apex Plaza, Forbury Road, Reading, RG1 1AX, England

そして、ジェフが引用していたメッセージ(私による)の残りの部分は次のとおりです。

I suppose the idea is that for the second 1,3cmd, line "1" has
not been seen, so the 1,3 range is not entered.

Same idea as in

printf '%s\n' A B C | sed -n '1d;1,2p'

whose behavior differ in traditional (heirloom toolchest at
least) and GNU.

It's unclear to me whether POSIX wants one behavior or the
other.

したがって、(Geoffによれば)POSIXはGNUの動作が非準拠であることは明らかです。

そして、範囲がどのように処理されるかを理解していない人にとって意外なほどに少なくても(Geoffでも最初に適合動作"strange"を見つけた)、一貫性が低くなります(と比較seq 10 | sed -n '1d;1,2p'seq 10 | sed -n '1d;/^1$/,2p'てください)。

誰もそれをバグとしてGNUの人々に報告することを気にしなかった。バグとみなすかどうかはわかりません。おそらく、最良のオプションは、POSIX仕様を更新して、両方の動作がどちらにも依存できないことを明確にできるようにすることです。

編集sed70年代後半からUnix V7の元の実装を見てきましたが、数値アドレスの動作が意図されていなかったか、少なくとも完全に考え抜かれていなかったようです。

逆に、Geoffが仕様(およびそれが発生する理由についての私の元の解釈)を読むと、次のようになります。

seq 5 | sed -n '3d;1,3p'

1、2、4、5行目が出力されるはずです。これは、今回の1,3pように、rangedコマンドが遭遇することのない最後のアドレスだからです。seq 5 | sed -n '3d;/1/,/3/p'

しかし、それは元の実装でも、私が試した他の実装でも起こりません(busybox sedは1、2、4 行目を返しますが、これはバグのように見えます)。

UNIX v7コードを見ると現在の行番号が(数値)終了アドレスより大きい場合をチェックし、範囲外になります。開始アドレスに対してそれを行わないという事実は、意図的な設計というよりも見落としのように見えます。

つまり、現時点では、POSIX仕様の解釈に実際に準拠している実装はないということです。

GNU実装でのもう1つの混乱する動作は次のとおりです。

$ seq 5 | sed -n '2d;2,/3/p'
3
4
5

2行目がスキップされたため、2,/3/3行目(番号が2以上の最初の行)に入力されます。しかし、それは私たち範囲に入るようにした行なので、それは終了アドレスをチェックされません。で悪化しbusybox sedます:

$ seq 10 | busybox sed -n '2,7d; 2,3p'
8

2行目から7行目が削除されたため、8行目が> = 2である最初の行なので、2,3の範囲が入力されます。


1
だから、問題はまだ解決されていないように思えます。それが起こった理由についてはあなたの推論に同意しますが、それが望んでいたものかどうかも不明です。正しかった。それもあなたの読書ですか?
マイケルホーマー

1
@MichaelHomer、アイデアは(Geoffによれば)POSIXはGNUの動作が非準拠であることは明らかであるということです。また、範囲がどのように処理されるかをユーザーが理解する可能性があまり高くない場合でも、一貫性が低くなります(と比較seq 10 | sed -n '1d;1,2p'してくださいseq 10 | sed -n '1d;/^1$/,2p')。誰もそれをバグとしてGNUの人々に報告することを気にしなかった。私はそれをバグとして認定するかどうかはわかりませんが、おそらく最良のオプションは、POSIX仕様を更新して、両方の動作がどちらにも依存できないことを明確にできるようにすることです。
ステファンChazelas

2
実際、POSIXの定義では、アドレス範囲を開始または終了するためにアドレスを「確認」する必要があるという記述がないため、GNU実装であるIMOはPOSIXの表現に厳密に従います(GNUには驚くべきことです)。これは、私が知っているほとんどの実際のケースで望ましい動作でもあります。しかし、あなたが指摘するように、それは一貫している必要があります。また、範囲のパターンを各行でチェックするとd、パフォーマンスの問題だけでなく、範囲に必要な「目に見えない」パターンが追加の空のパターンに影響を与えることができないため、実装上の問題が発生します。
フィリポス

@Philippos、その1d;1,2pスクリプトでは、1,2pコマンドは最初の行では実行されないため、最初のアドレスパターンスペースと一致しません。これは、そのテキストを解釈する1つの方法です。いずれの場合でも、アドレスの評価はコマンドの実行時に行う必要があることは明らかです。Likesed 's/./x/g; /xxx/,/xxx/d'
StéphaneChazelas

2
@Isaac、それが問題の核心です。POSIX言語1/1/は両方のアドレスで1あり、行番号が1の/1/場合のアドレスは、パターンスペースに含まれる場合のアドレスは、1両方のタイプのアドレスを同じように扱うべきか、または行番号の範囲を「彼らが一致したかどうかに関係なく、絶対に」。より歴史的な文脈については、私の最新の編集も参照してください。
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.