使用sed替换引号中的文本

Jus*_*ner 9 regex sed

我有这个测试文件.

[root@localhost ~]# cat f.txt 
"a aa"  MM  "bbb  b"
MM    MM
MM"b b "
[root@localhost ~]#

我想替换引号中的所有空格字符,注意,仅在引号中.不应触及引号中的所有字符.也就是说,我想要的是类似于:

"a_aa"  MM  "bbb__b"
MM    MM
MM"b_b_"

这可以用sed实现吗?

谢谢,

Jon*_*ler 8

这是一个完全不平凡的问题.

这可以用下划线替换引号内的第一个空格:

$ sed 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa"  MM  "bbb_ b"
MM    MM
MM"b_b "
$
Run Code Online (Sandbox Code Playgroud)

对于此示例,如果任何引号内部的空格不超过两个,则只需重复该命令即可,但结果不正确:

$ sed -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' \
>     -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa"_ MM  "bbb_ b"
MM    MM
MM"b_b_"
$
Run Code Online (Sandbox Code Playgroud)

如果你的sed支持版本的扩展正则表达式',那么这适用于示例数据:

$ sed -E \
>    -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
>    -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
>    -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
>    f.txt
"a_aa"  MM  "bbb__b"
MM    MM
MM"b_b_"
$
Run Code Online (Sandbox Code Playgroud)

你必须为双引号内的每个空间重复那个可怕的正则表达式 - 因此对于第一行数据是三次.

正则表达式可以解释为:

  • 从一行开始,
  • 查找"零或多个非引号的序列,可选地后跟引号,没有空格或引号,以及引号",整个程序集重复零次或多次,
  • 后面跟一个引号,零个或多个非引号,非空格,一个空格,零个或多个非引号和引号.
  • 将匹配的材料替换为引导部分,当前引用通道开始处的材料,下划线和当前引用通道的尾随材料.

由于启动锚,每个空白必须重复一次......但是sed有一个循环结构,所以我们可以这样做:

$ sed -E -e ':redo
>            s/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/
>            t redo' f.txt
"a_aa"  MM  "bbb__b"
MM    MM
MM"b_b_"
$
Run Code Online (Sandbox Code Playgroud)

所述:redo定义的标签; 该s///命令是如前; t redo如果自上次读取行或跳转到标签以来已完成任何替换,则命令跳转到标签.


鉴于评论中的讨论,有几点值得一提:

  1. -E选项适用sed于MacOS X(已测试10.7.2).GNU版本的相应选项sed-r(或--regex-extended).该-E选项与grep -E(也使用扩展正则表达式)一致."经典Unix系统"不支持ERE sed(Solaris 10,AIX 6,HP-UX 11).

  2. 您可以替换?我使用的(这是强制使用ERE而不是BRE的唯一字符)*,然后处理括号(在BRE中需要使用反斜杠以使它们成为捕获括号) ,离开剧本:

    sed -e ':redo
            s/^\(\([^"]*\("[^ "]*"\)*\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
            t redo' f.txt
    
    Run Code Online (Sandbox Code Playgroud)

    这在同一输入上产生相同的输出 - 我在输入中尝试了一些稍微复杂的模式:

    "a aa"  MM  "bbb  b"
    MM    MM
    MM"b b "
    "c c""d d""e  e" X " f "" g "
     "C C" "D D" "E  E" x " F " " G "
    
    Run Code Online (Sandbox Code Playgroud)

    这给出了输出:

    "a_aa"  MM  "bbb__b"
    MM    MM
    MM"b_b_"
    "c_c""d_d""e__e" X "_f_""_g_"
     "C_C" "D_D" "E__E" x "_F_" "_G_"
    
    Run Code Online (Sandbox Code Playgroud)
  3. 即使使用BRE表示法,也sed支持使用\{0,1\}符号指定前一个RE术语的0或1次出现,因此?可以使用以下方法将版本转换为BRE:

    sed -e ':redo
            s/^\(\([^"]*\("[^ "]*"\)\{0,1\}\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
            t redo' f.txt
    
    Run Code Online (Sandbox Code Playgroud)

    这产生与其他替代品相同的输出.