匹配第一个双括号而不是最后一个
Match first double parenthesis instead of last
我尝试了很长时间来替换:
(a (b ((c) (d)) (e) :hello ((f (g) h)))))
来自
(a (b ((c) (d)) (e)))
hello
没有出现在字符串的其他任何地方。如果尝试了很多不同的东西但认为它应该像这样工作:
sed -i 's/\s:hello\s.*))//g'
然而,它似乎与前两个括号不匹配,即
(a (b ((c) (d)) (e) :你好 ((f (g) h ))))
但最后两个
(a (b ((c) (d)) (e) :你好 ((f (g) h)))))
从而删除 :hello
.
之后的所有内容
我也试过使用 [^)]*
但只能让它带一个括号而不是两个并且因为在 g
之后有一个右括号所以它停在那里。
perl
更适合这里,因为它支持非贪婪匹配。下面的命令将匹配 hello
之后第一次出现的 ))
:
$ s='(a (b ((c) (d)) (e) :hello ((f (g) h))))'
$ echo "$s" | perl -pe 's/\s:hello\s.*?\)\)//'
(a (b ((c) (d)) (e)))
# you can also recursively match balanced parentheses
$ cat ip.txt
(a (b ((c) (d)) (e) :hello ((f (g) h))))
(a (b ((c) (d)) (e) :hello (f (g) h)))
(a (b ((c) (d)) (e) :hello (f h)))
(a (b ((c) (d)) (e) :hello ((f ((c) (d)) h))))
$ perl -pe 's/\s:hello\s(\((?:[^()]++|(?1))++\))//' ip.txt
(a (b ((c) (d)) (e)))
(a (b ((c) (d)) (e)))
(a (b ((c) (d)) (e)))
(a (b ((c) (d)) (e)))
您可以使用一些技巧让它与 sed
一起工作。在下面的解决方案中,所有出现的 ))
首先替换为换行符(因为在默认用法中该字符不能成为输入行的一部分)。 [^\n]
现在只能用于匹配第一次出现。之后,将所有换行符改回 ))
.
$ s='(a (b ((c) (d)) (e) :hello ((f (g) h))))'
$ echo "$s" | sed 's/))/\n/g; s/\s:hello\s[^\n]*\n//; s/\n/))/g'
(a (b ((c) (d)) (e)))
如果您的数据与您的样本相似,那么您可以匹配从冒号到最后一个字母后的最后 3 个括号的所有内容,并用任何内容替换它。
使用sed
sed 's/ :.*[a-z])))//' input_file
(a (b ((c) (d)) (e)))
.*
表示“尽可能跳过”。如果你不是这个意思,请不要使用它。
就像您已经发现的那样,“不是右括号”的正则表达式是 [^)]
。但是,您希望允许使用一个括号,只要它后面没有紧跟另一个括号即可。这变得有点丑陋,因为您需要 \(...\|...\)
替代方案。 (切换到 sed -r
或 sed -E
并不会真正改善这种情况,因为虽然你可以在这个结构中避免使用反斜杠,但你必须使用反斜杠或以其他方式转义文字括号,字符 类.)
sed 's/\s:hello\s\([^)]\|)[^)]\)*))//g'
-i
选项在这里没有意义(如果你确实有一个文件要处理并且你想就地处理它,也许把它放回去)并且 \s
不是便携(切换到 [[:space:]]
相当于 POSIX)。
如另一个答案所述,更现代的正则表达式工具提供了尽可能少跳过的非贪婪量词。考虑清楚地表达一个精确的需求仍然是件好事;非贪婪匹配只是实现精确的另一种工具。太多的初学者感到困惑,并将其用作“按我的意思去做”的锤子,当然它根本不是。
我尝试了很长时间来替换:
(a (b ((c) (d)) (e) :hello ((f (g) h)))))
来自
(a (b ((c) (d)) (e)))
hello
没有出现在字符串的其他任何地方。如果尝试了很多不同的东西但认为它应该像这样工作:
sed -i 's/\s:hello\s.*))//g'
然而,它似乎与前两个括号不匹配,即
(a (b ((c) (d)) (e) :你好 ((f (g) h ))))
但最后两个
(a (b ((c) (d)) (e) :你好 ((f (g) h)))))
从而删除 :hello
.
我也试过使用 [^)]*
但只能让它带一个括号而不是两个并且因为在 g
之后有一个右括号所以它停在那里。
perl
更适合这里,因为它支持非贪婪匹配。下面的命令将匹配 hello
之后第一次出现的 ))
:
$ s='(a (b ((c) (d)) (e) :hello ((f (g) h))))'
$ echo "$s" | perl -pe 's/\s:hello\s.*?\)\)//'
(a (b ((c) (d)) (e)))
# you can also recursively match balanced parentheses
$ cat ip.txt
(a (b ((c) (d)) (e) :hello ((f (g) h))))
(a (b ((c) (d)) (e) :hello (f (g) h)))
(a (b ((c) (d)) (e) :hello (f h)))
(a (b ((c) (d)) (e) :hello ((f ((c) (d)) h))))
$ perl -pe 's/\s:hello\s(\((?:[^()]++|(?1))++\))//' ip.txt
(a (b ((c) (d)) (e)))
(a (b ((c) (d)) (e)))
(a (b ((c) (d)) (e)))
(a (b ((c) (d)) (e)))
您可以使用一些技巧让它与 sed
一起工作。在下面的解决方案中,所有出现的 ))
首先替换为换行符(因为在默认用法中该字符不能成为输入行的一部分)。 [^\n]
现在只能用于匹配第一次出现。之后,将所有换行符改回 ))
.
$ s='(a (b ((c) (d)) (e) :hello ((f (g) h))))'
$ echo "$s" | sed 's/))/\n/g; s/\s:hello\s[^\n]*\n//; s/\n/))/g'
(a (b ((c) (d)) (e)))
如果您的数据与您的样本相似,那么您可以匹配从冒号到最后一个字母后的最后 3 个括号的所有内容,并用任何内容替换它。
使用sed
sed 's/ :.*[a-z])))//' input_file
(a (b ((c) (d)) (e)))
.*
表示“尽可能跳过”。如果你不是这个意思,请不要使用它。
就像您已经发现的那样,“不是右括号”的正则表达式是 [^)]
。但是,您希望允许使用一个括号,只要它后面没有紧跟另一个括号即可。这变得有点丑陋,因为您需要 \(...\|...\)
替代方案。 (切换到 sed -r
或 sed -E
并不会真正改善这种情况,因为虽然你可以在这个结构中避免使用反斜杠,但你必须使用反斜杠或以其他方式转义文字括号,字符 类.)
sed 's/\s:hello\s\([^)]\|)[^)]\)*))//g'
-i
选项在这里没有意义(如果你确实有一个文件要处理并且你想就地处理它,也许把它放回去)并且 \s
不是便携(切换到 [[:space:]]
相当于 POSIX)。
如另一个答案所述,更现代的正则表达式工具提供了尽可能少跳过的非贪婪量词。考虑清楚地表达一个精确的需求仍然是件好事;非贪婪匹配只是实现精确的另一种工具。太多的初学者感到困惑,并将其用作“按我的意思去做”的锤子,当然它根本不是。