テキストファイルがあり、内部のすべてのスペースをハイフンに置き換えたい[[
(]]
括弧は入れ子ではなく常に一致します)。以下は例です。
$ cat test.txt
abc [[foo]] xyz
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz [[something else]]
したがって、希望の出力は次のようになります。
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
sed
私は角かっこ内の文字列を一致させ、置換するフラグでe
結果を再実行するために使用できると思いました。sed
しかし、問題は、一致する文字列がコマンドとして実行されるだけでなく、全体のパターンスペース(全体行のように見えます)も実行されることです。
$ sed -E 's@(\[\[)(.+)(\]\])@sed -e "s/ /-/g" <<< "\1\2\3"@gpe' test.txt
abc sed -e "s/ /-/g" <<< "[[foo]]" xyz
sh: 1: Syntax error: redirection unexpected
abc sed -e "s/ /-/g" <<< "[[foo bar]]" xyz
sh: 1: Syntax error: redirection unexpected
abc sed -e "s/ /-/g" <<< "[[foo bar baz]]" xyz
sh: 1: Syntax error: redirection unexpected
e
フラグを介して実行される項目を一致する文字列に制限する方法はありますか?そうでない場合は、この問題をどのように解決する必要がありますかsed
?
答え1
シェルに渡される修飾子を制限する方法はありませんが、e
次のようにできます。
$ sed -E ':a;s@(.*\[\[)([^][]* [^][]*)(\]\].*)@printf "%s%s%s" "\1" "$(printf "\2" | sed "s/ /-/g")" "\3"@e;ta' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
複数の置換処理はループで行われ、一致の欲張りな性質のため、実際には置換が逆の順序で実行される。
また、whichをe
使用すると入力リダイレクトを/bin/sh
サポートしない可能性があることに注意してください<<<
(したがって同等のパイプを使用してくださいprintf "\2" | sed "s/ /-/g"
)。
Perlがオプションの場合は、次のように元の意図に近い操作を実行できます。
$ perl -pe 's/(?<=\[\[)(.*?)(?=\]\])/$1 =~ s: :-:rg/ge' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
Perlは非グリッド修飾子を提供するので?
、g
外部置換のフラグを使用して、1行に複数の置換をより一般的に処理できます。
答え2
使用標準sed
:
$ sed -e ':again' -e 's/\(\[\[[^]]*\) \([^]]*\]\]\)/\1-\2/g' -e 't again' file
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]
[[
]]
これにより、との間にあるすべての空白文字がダッシュに置き換えられます。これは、との間の空白文字(オプションで別の文字列の横に位置)を[[
一致させることによって行われます。]]
一致する部分文字列はそのまま置き換えられ、スペースはダッシュで置き換えられます。
交換が完了したら、このコマンドを使用すると、スクリプトは別の交換のためにラベルt
に分岐します。again
これは一致が重なっているため、最初に見逃したスペースを説明できます。
なぜなら、すべての人は常に(おそらく)[[
関連があるというからです。]]
同じ行に)、コマンドを少し短くすることができます。
sed -e ':again' -e 's/\(\[\[[^]]*\) /\1-/g' -e 't again' file
これは終了を探していません]]
。
答え3
cat - <<\! > file
Abc [[ \ ]] def and a cup of
Ghi [[]] jkl
Mno [[ ]] pqr
abc [[" \' \\\"]] xyz
abc [[foo$$]] xyz [[a b c]] deal
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz
abc [[foo $bar baz]] xyz [[FOO BAR VAZ]] $#
!
GNU sed
/e
修飾子に頼る必要はありません。
sed -Ee '
:loop
s/([[]{2}[^][]*) ([^]]*]])/\1-\2/
t loop
' file
Posixly方式で書くのは簡単ですが、拡張正規表現モードが有効になっているときに使用されるバックスラッシュを最小限に抑えるために、-E
ループは「[[...]]」ペア空白文字の発見/反復を徐々に切り替えます。どのペアでもそのようなスペースが見つからない場合、ループは停止します。次にパターンスペースを印刷し、次の行をパターンスペースに読み込みます...リンス... eofが表示されるまで繰り返します。
- |ユーティリティを使用して
awk
文字列の各行を分割します。これは対称性([[n]])がペアでこの順序で発生するためです。ぶら下がっている[[または]]はありません。その後、すべての偶数フィールドは[[n]]内にあり、処理する必要があります。[[
]]
awk -F '[[]{2}|]]' '
{
for (i=2; i<=NF; i+=2) {
gsub(/ /, "-", $i)
$i = "[[" $i "]]"
}
}1
' OFS= file
3. 修飾子をGNU sed
使用します/e
。
sed -Ee "
s/'/&\"&\"&/g;tloop
:loop
s|(.*[[]{2})([^][]* [^]]*)(]].*)|v='\2';v=\${v// /-};printf '%s' '\1' \"\$v\" '\3'|e
t loop
" file
- 上記のawkと同じ方法でPerlを使用します。
perl -F'(\[\[|]])' -lane 'my $i;
print map { ++$i%4 == 3 ? tr/ /-/r : $_ } @F;
' file
- GNU sedは[[...]]ペアを切り取り、再びマージして分離ペアを変換します。すべてのペアが検査されるまでこの作業を続けます。
m='[^\n]'
sed -Ee "
s/[[]{2}|]]/&\n/g;T;h
:loop
s/^$m*\n($m*)\n.*/\1/
y/ /-/;G
s/^($m*)\n($m*)\n$m*\n/\2\1/
h
/\n/b loop
" file
出力:
Abc [[-\--]] def
Ghi [[]] jkl
Mno [[-]] pqr
abc [["-\'-\\\"]] xyz
abc [[foo$$]] xyz [[a-b-c]] deal
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz
abc [[foo-$bar-baz]] xyz [[FOO-BAR-VAZ]] $#