別のコマンドでパイピングするときは、生のパターンスペースを一致する文字列に制限します。

別のコマンドでパイピングするときは、生のパターンスペースを一致する文字列に制限します。

テキストファイルがあり、内部のすべてのスペースをハイフンに置き換えたい[[]]括弧は入れ子ではなく常に一致します)。以下は例です。

$ 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]] $#
!
  1. GNU sed/e修飾子に頼る必要はありません。
sed -Ee '
  :loop
    s/([[]{2}[^][]*) ([^]]*]])/\1-\2/
  t loop
' file

Posixly方式で書くのは簡単ですが、拡張正規表現モードが有効になっているときに使用されるバックスラッシュを最小限に抑えるために、-Eループは「[[...]]」ペア空白文字の発見/反復を徐々に切り替えます。どのペアでもそのようなスペースが見つからない場合、ループは停止します。次にパターンスペースを印刷し、次の行をパターンスペースに読み込みます...リンス... eofが表示されるまで繰り返します。


  1. |ユーティリティを使用して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

  1. 上記のawkと同じ方法でPerlを使用します。
perl -F'(\[\[|]])' -lane 'my $i;
  print map { ++$i%4 == 3 ? tr/ /-/r : $_ } @F;
' file

  1. 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]] $#

関連情報