我尝试使用sed在一串非空白字符的每个大写字母前面插入_
,除非它在开头。 (我想转换在camelcase中的字符串,偶尔包含几个相邻的大写字母甚至是标点符号。)
期望的行为:
输入:
AaAaAa AAA AAA
输出:
Aa_Aa_Aa A_A_A A_A_A
我尝试使用以下命令:
sed -e "s/\(\S\)\([[:upper:]]\)/\1_\2/g"
但它在上面输入的最后两个字符串失败了,产生了这个:
Aa_Aa_Aa A_AA A_AA
我不明白为什么。
我使用的是GNU sed 4.2.2。
答案 0 :(得分:2)
我假设您的示例输入错误,因为您提供的替换Aa Aa Aa
没有做任何事情。它也不是驼峰案例标识符。它应该是AaAaAa
,对吗?
如果是这样,那么你可以让sed
通过使它循环直到不再发生替换来做你需要的事情:
echo "AaAaAa AAA AAA" | sed -e ':x;s/\([^[:space:]_]\)\([[:upper:]]\)/\1_\2/g;tx'
产生
Aa_Aa_Aa A_A_A A_A_A
答案 1 :(得分:2)
这可能适合你(GNU sed):
sed -r 'y/_/\n/;s/[[:upper:]]/_&/g;s/\b_//g;y/\n/_/' file
将所有_
转换为唯一替代。插入_
的大写字符前面。删除所有前导_
。重新转换原始_
。
如果您首先没有任何前导_
,那么这就足够了:
sed -r 's/[[:upper:]]/_&/g;s/\b_//g' file
答案 2 :(得分:1)
问题在于,使用单个s///g
时,正则表达式匹配不能重叠(并且未考虑更早匹配的结果)。
使用AAA
,第一场比赛是
AAA
^^
| \
\1 \2
更换后,我们有A_AA
,其中包含"当前位置"在最右边的两个A之间:
A _ A A
^
next match attempt starts here
然后我们再次尝试匹配,但我们已经没有人物了。 \S
与上一个A
匹配,但那就是:之后没有大写字符。
为了完成这项工作,我们必须以某种方式将中间A
与第一次替换的\2
和第二次替换的\1
进行匹配,并且我不会# 39;不知道如何用sed做到这一点。
(使用perl会很容易,因为你可以使用后视/前瞻,不会在匹配中包含周围的文字:perl -pe 's/(?<=\S)(?=[[:upper:]])/_/g'
)