我有一个shell变量:
all_apk_file="a 1 2.apk x.apk y m.apk"
我想使用以下命令将a 1 2.apk
替换为TEST
:
echo $all_apk_file | sed 's/(.*apk ){1}/TEST/g'
.*apk
表示以apk
结尾,{1}
表示只匹配一次,但它不起作用;我只将原始变量作为输出:a 1 2.apk x.apk y m.apk
谁能告诉我为什么?
答案 0 :(得分:2)
首先,要启用sed
中您熟悉的正则表达式,您需要使用-r
开关(sed -r ...):
echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/g'
# returns TESTy m.apk
查看它返回的内容:TESTy m.apk
。这是因为.*
贪婪,所以它与尽可能匹配。也就是说,.*
匹配a 1 2.apk x
,并且您说要替换.*apk
,a 1 2.apk x.apk
替换为'TEST',结果为TESTy m.apk
(请注意正则表达式中“.apk”之后的以下空格,这就是为什么匹配不会一直延伸到最后一个'.apk'的原因,后面没有空格。)
通常可以将.*
更改为.*?
以使其非贪婪,但sed不支持此行为。
所以,要修复它,你只需要让你的正则表达式更具限制性。
很难说出你想做什么 - 删除前三个单词,其中第三个单词以'.apk'结尾,并替换为'TEST'?在这种情况下,可以使用正则表达式:
[a-z0-9]+ +[a-z0-9]+ +[a-z0-9]+\.apk
与'i'开关组合(不区分大小写)。
你必须给出决定删除内容的逻辑(前三个单词,任意数量的单词直到第一个'.apk'单词等),以便我们帮助你进一步使用正则表达式。
其次,您已将'g'开关置于正则表达式中。这意味着所有匹配模式将被替换,您似乎只希望第一个被替换。所以删除'g'开关。
最后,所有这些组合:
echo $all_apk_file | sed -r 's/[a-z0-9]+ +[a-z0-9]+ +[a-z0-9]+\.apk/TEST/i'
# TEST x.apk y m.apk
答案 1 :(得分:1)
这可能对您有用:
echo "$all_apk_file" | sed 's/apk/\n/;s/.*\n/TEST/'
TEST x.apk y m.apk
至于为什么你的正则表达式不起作用,请参阅@ mathematicalcsoffee和@Jonathan Leffler的优秀解释。
s/apk/\n/
与s/apk/\n/1
同义,这意味着将apk
的第一次出现替换为\n
。由于sed使用\n
作为记录分隔符,我们知道它不会出现在传递给sed命令的任何初始字符串中。有了这两个事实,我们可以分裂。
N.B。如果您想要替换第二个apk
,那么s/apk/\n/2
将适合该帐单。当然,对于apk
的最后一次出现,然后.*apk
开始发挥作用。
答案 2 :(得分:0)
问题的一部分是,在常规sed
中,()
和{}
是模式中的普通字符,直到使用反斜杠进行转义。由于变量值中没有括号,因此正则表达式永远不会匹配。使用GNU sed
,您还可以使用-r
标志启用扩展正则表达式。如果您解决了这个问题,那么您将遇到.*
贪婪的问题,g
修饰符实际上不会改变任何内容:
$ echo $all_apk_file | sed 's/\(.*apk \)\{1\}/TEST/g'
TESTy m.apk
$ echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/g'
TESTy m.apk
$ echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/'
TESTy m.apk
$
它只会在那里停止,因为在变量的回显值中m.apk
之后没有空格。
现在的问题是:你想要替换的是什么?这听起来像'一切都包含在一个单词末尾的apk
的第一次出现。这可能最容易通过Perl正则表达式中的尾随上下文或非贪婪匹配来完成。如果可以选择切换到Perl,请执行此操作。如果不是,那么在正常的sed
正则表达式中这不是微不足道的。
$ echo $all_apk_file | sed 's/^[^.]* [^.][^.]*\.apk /TEST /'
TEST x.apk y m.apk
$
这会查找没有点的任何内容,后面跟一个空格,然后再没有点,.apk
;这意味着允许的第一个点是2.apk
中的点。它适用于样本数据;如果变量包含:
all_apk_file="a 1.2 2.apk m.apk y.apk 37"
您需要对其进行调整以满足您的要求。