-z
sedを使用すると、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コマンドはfoo
withの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
。
-ジ
-z
ifを使用すると、行は(\0
not \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が\0
aを時々区切り記号として扱い、\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
て何度も一致し、g
slurp内で一致するのをm
助け、カウンタ変数が2の場合にのみ評価します。^
e
bar
答え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