仅在令牌之间替换文本(单行)

时间:2018-05-12 22:12:19

标签: sed

我想删除方括号之间的字符串中的空格,并使用单行输入。

更确切地说,匹配\[[a-zA-Z0-9 ,]+\]的字符串(无括号的字母逗号和空格,方括号之间)

例如:

[ "This is a test": [a, b, c] ]

应该成为:

[ "This is a test": [a,b,c] ]

我尝试了几次分支尝试,但找不到有效的语法。

例如:

/\[[a-zA-Z ,]\+\]/ba; :a;s/ //g;

但这会替换整行的空格,因为sed是基于行的(我的输入是单行)。

我还尝试了;e命令,如果整个字符串以echo "为前缀并以"为后缀,那么该命令会起作用,但那会是单/双引号逃脱地狱(整个字符串可能包含'")。

GNU sed是受欢迎的,但我想保持最小的依赖,所以没有perl除非需要,没有ruby,python,php ......

确实,我知道以下工作完美,但php是一个太大的依赖:

echo preg_replace_callback(
    "/\[[a-zA-Z ,]+\]/",
    function ($m) { return str_replace(" ", "", $m[0]); },
    '{"a":{"a":{"a":"a b c"},"b":{"b":[a, b]}}}'
);

输出:

{"a":{"a":{"a":"a b c"},"b":{"b":[a,b]}}}

1 个答案:

答案 0 :(得分:2)

第一次通过 - 它有效,但它很笨拙

这是一个适用于GNU和BSD sed的解决方案:

sed -E \
    -e '/\[[[:alnum:] ,]+\]/ {
            s/\[([[:alnum:] ,]+)\]/^B\1^E/
            :a
            s/(^B[[:alnum:],]*) +/\1/
            t a
            s/^B/[/
            s/^E/]/
        }' \
    data

^B^E的外观是控制字符(原始中的 Control-B Control-E )将出现在实际文本中。 (首次复制时,我显示^B显示为,^E显示为。)

说明:

  • /\[[[:alnum:] ,]+\]/ { - 匹配包含方括号和字母数字加上空格和逗号的行,并执行从{到匹配的}的操作序列。
  • s/\[([[:alnum:] ,]+)\]/^B\1^E/ - 用控制字符替换方括号。
  • :a - 设置标签
  • s/(^B[[:alnum:],]*) +/\1/ - 替换一个^B加上一系列字母数字或逗号(被捕获)和一个只有捕获的一个或多个空格的字符串。
  • t a - 如果s///命令进行了更改,请跳回标签a
  • s/^B/[/ - 用方括号替换^B
  • s/^E/]/ - 将^E替换为方括号。
  • } - 已完成

分支是必要的,因为通常情况下,s///运算符不会重新扫描它刚刚替换的素材,而重新扫描它是至关重要的。

考虑到更广泛的输入文件:

\[[a-zA-Z0-9 ,]+\] (caseless alphanum comma and space, between square brackets)

For example:

[ "This is a test": [a, b c] ]
[ "This is a test": [a, b, c] ]
[ "This is test 3": [  XXX,    YYY,   XXX    ] ]

Should become:

[ "This is a test": [a,bc] ]
[ "This is a test": [a,b,c] ]
[ "This is test 3": [XXX,YYY,XXX] ]

脚本生成:

\[[a-zA-Z0-9 ,]+\] (caseless alphanum comma and space, between square brackets)

For example:

[ "This is a test": [a,bc] ]
[ "This is a test": [a,b,c] ]
[ "This is test 3": [XXX,YYY,XXX] ]

Should become:

[ "This is a test": [a,bc] ]
[ "This is a test": [a,b,c] ]
[ "This is test 3": [XXX,YYY,XXX] ]

第二次通过 - 审查和完善

是值得的

看着它,^E不是必需的,也许不是^B。上面的版本只处理该行上的第一组这样的方括号。您需要更灵敏的探测器正则表达式(在标记之间至少保留一个空格的那些)才能在一条线上处理多个这样的模式。

例如:

sed -E \
    -e ':a
        /\[[[:alnum:],]* [[:alnum:] ,]*\]/   s/(\[[[:alnum:],]*) +/\1/
        t a' \
    data

说明:

  • :a - 设置标签
  • /\[[[:alnum:],]* [[:alnum:] ,]*\]/ - 如果该行包含一个空方括号,零个或多个字母数字或逗号字符,一个或多个空格,以及零个或多个字母数字或逗号或空格,后跟紧密方格支架,然后......
  • s/(\[[[:alnum:],]*) +/\1/ - 只用非空格替换零个或多个字母数字或逗号字符的空方和序列以及一个或多个空格,然后......
  • t a - 如果已完成替换,则跳转到标签a

假设:

[ "This is a test": [a, b c] ]
[ "This is test 2": [a, b, c] ]
[ "This is test 3": [  XXX   ,    YYY   ,   XXX    ] ]
[ "This is test 4": [  XXX   ,    YYY   ,   XXX    ] [ 1 , 2 , 3 ] ]
[ "This is test 5": [  XXX   ,    YYY   ,   XXX    ] [ 1 , 2 , 3 ] [ abc ] [ ] ]

这会产生:

["This is a test": [a,bc] ]
["This is test 2": [a,b,c] ]
["This is test 3": [XXX,YYY,XXX] ]
["This is test 4": [XXX,YYY,XXX] [1,2,3] ]
["This is test 5": [XXX,YYY,XXX] [1,2,3] [abc] [] ]

这大致相当于answerBeta;可以通过在替换命令之前消除匹配并修改(略微复杂化)替换以使其与Beta的工作匹配来进一步简化。