-ジ

-ジ

-zsedを使用すると、nullデータパターン()を使用してn番目のパターン発生を一致させることができることを学びましたsed -z 's/foo/bar/2'

を使用するときに行の先頭を一致させる方法は-z

私が実行している場合:

echo $'foo\nfoo\nfoo' | sed -z 's/^foo/baz/2'

^文字列全体の先頭が表示されるため、置換はありません。

$ echo $'foo\nfoo\nfoo' | sed -z 's/^foo/baz/g'
baz
foo
foo

Perlには次のものがあります。m正規表現修飾子(音を出すとき)しかし、sedでは役に立ちません。

答え1

echo $'foo\nfoo\nfoo' | sed -Ez 's/(^|\n)foo/\1baz/2'

^最初の行の先頭に何が起こるかを正確に計算する必要がありますfoo

コードでは(^|\n)fooなくの発生回数を計算しますfoo。 sを計算したいのですが、foo交換したい場合ただ目的の項目が行の先頭にある場合、このコードは解決策ではありません。たとえば、

echo $'foo foo\nfoo foo\nfoo foo' | sed -Ez 's/(^|\n)foo/\1baz/3'

彼が交換されたのはfoo今回が三番目ではないfoo

GNU 4.8を使用してsedテストされました。

答え2

選択した答えが見えるほど単純ではありません。まず、2(またはs / / / 2の他の数字)は実際に何を意味しますか?これは、適用される行の2番目の正規表現の一致が変更されることを意味します。

存在する

$ printf '%s\n' 'foo foo foo' 'foo foo foo' 'foo foo foo' | sed 's/foo/bar/2'
foo bar foo
foo bar foo
foo bar foo

sedコマンドはfoowithの2番目(2)インスタンスを変更しますbar適用される行(すべての行)1行または複数行でのみ機能するように変更できます。

printf '%printf '%s\n' 'foo foo foo'{,,,,} | sed '3,4s/foo/bar/2'
foo foo foo
foo foo foo
foo bar foo
foo bar foo
foo foo foo

行全体ではなく行3合計のみ4が変更され、これらすべての行の中でfoo変更されるインスタンスは2番目の行(2)です。

これがうまくいく方法ですs/foo/bar/2

-ジ

-zifを使用すると、行は(\0not \n)で終わります。ただし、置換はまったく同じように機能します(\0代替使用\n)。

$ printf '%s\0' 'foo foo foo'{,,,,} | sed -z '3,4s/foo/bar/2' | xxd
00000000: 666f 6f20 666f 6f20 666f 6f00 666f 6f20  foo foo foo.foo 
00000010: 666f 6f20 666f 6f00 666f 6f20 6261 7220  foo foo.foo bar 
00000020: 666f 6f00 666f 6f20 6261 7220 666f 6f00  foo.foo bar foo.
00000030: 666f 6f20 666f 6f20 666f 6f00            foo foo foo.

\0と\nを混ぜてください。

そこにはecho $'foo\nfoo\nfoo' | sed -z 's/^foo/baz/2'十分ではありません。foo各ライン2番目のものを変更できますが、次の例では変更する必要がありますか?

$ printf 'foo foo foo\nfoo foo foo\n' | sed -z 's/^foo/baz/2'
foo foo foo
foo foo foo

fooこんな、いや、最初にも足りません。問題は、行がどこから始まるかです。改行やa、\0またはその両方にありますか?それとも?

^「-z」を使用する場合、「行の先頭」を考慮することは意味がありません。

これはsedの内部混乱です。覚えておいてください:使用法-zは実験的であり、奇妙な問題を引き起こす可能性があります。

パターン空間

実際、置換が正しく機能するためには、入力全体がパターン空間に存在する必要があります。いいえ、入力に NUL( ) がある場合は効果がありません。\0これは行区切り文字(またはawk用語でレコード区切り文字)として扱われます。

$ printf 'foo\0foo\0foo\0' | sed -z 's/^foo/baz/2'
foofoofoo

sedのパターンスペース内で入力ファイル全体を使用してからH;1h;$!d;x;.....置き換え^fooを試みることができます。

$ printf 'foo\0foo\0foo\0\n' | sed -z 'H;1h;$!d;x;l;s/^foo/ baz /M2'
foo\000foo\000foo\000\n$foo baz foo

lパターン空間内に何があるのか​​を確認し、最初の行よりも一致するMにはこのフラグが必要です。使用しない場合、^最初の行(パターンスペースの先頭)のみが一致します。M^foo

選択肢は次Mのとおりです。

$ printf 'foo\0foo\0foo\0' | sed -z 'H;1h;$!d;x;l;s/\(^\|\x0\)foo/ baz /2'
foo\000foo\000foo$foo baz foo

入力に明示的に提供された末尾が不足している\0内部パターン空間に入力すると、末尾は削除されます。foo\000foo\000foo\0

\0末尾の改行を追加すると、3つすべてを取得できます。

$ printf 'foo\0foo\0foo\0\n' | sed -z 'H;1h;$!d;x;l;s/\(^\|\x0\)foo/ baz /2'
foo\000foo\000foo\000\n$foo baz foo

これは、sedが\0aを時々区切り記号として扱い、\n他の場合にaを区切り文字として扱うことを明らかに示しています。

簡単に言えば、-zこのオプションを使用したsedはまだ実験的です。

答え3

Perlには(フルルック音を出すとき)正規表現修飾子がありますが、m役に立ちません。

確信している。

printf '%s\n' foo foo foo |\
perl -0777 -pe 's/^(foo)/++$c == 2 ? "bar" : $1/egm'

私たちはslurpを使用し-0777て何度も一致し、gslurp内で一致するのをm助け、カウンタ変数が2の場合にのみ評価します。^ebar

答え4

slurpモード(-z)では、GNU sedはレコード区切り文字をnull値として扱います。しかし、ASCIIテキストファイルにはヌル文字がないため、ファイル全体は本質的にsedのレコードまたは行です。この問題を解決するには、最初にすべての改行文字(\ n)を行区切り文字(NUL)に変更してから、2番目の一致にs / / in t複数行パターンを適用します。最後に逆変換を行います。

printf '%s\n' foo foo foo |
sed -z '
  y/\n/\x00/
  s/^foo/BAR/M2
  y/\x00/\n/
'

出力:

foo
BAR
foo

関連情報