我只想获取一个可能是也可能不是gzip的文件的编号。但是,似乎sed中的正则表达式不支持?
。这是我试过的:
echo 'file_1.gz'|sed -n 's/.*_\(.*\)\(\.gz\)?/\1/p'
并没有返回任何内容。然后我在正在分析的字符串中添加了?
:
echo 'file_1.gz?'|sed -n 's/.*_\(.*\)\(\.gz\)?/\1/p'
得到了:
1
所以,看起来大多数正则表达式中使用的?
在sed中不受支持,对吧?那么,我希望sed为1
和file_1
提供file_1.gz
。如果执行时间至关重要,那么在bash脚本中执行此操作的最佳方法是什么?
答案 0 :(得分:34)
等同于x?
的是\(x\|\)
。
但是,许多版本的sed都支持启用“扩展正则表达式”的选项,其中包括?
。在GNU sed中,标志为-r
。请注意,这也会改变未转义的parens进行分组。例如:
echo 'file_1.gz'|sed -n -r 's/.*_(.*)(\.gz)?/\1/p'
实际上,你的正则表达式中还有另一个错误,就是如果有的话,那些parens中的贪婪.*
会吞噬“.gz”。据我所知,sed没有与*
相当的非贪婪,但您可以使用|
解决此问题。 sed中的|
(以及许多其他正则表达式实现)将使用最左边的匹配,因此您可以执行以下操作:
echo 'file_1.gz'|sed -r 's/(.*_(.*)\.gz)|(.*_(.*))/\2\4/'
这会尝试与.gz匹配,只有在没有它的情况下才会尝试。实际上只存在第2组或第4组中的一个(因为它们位于相同的|
的相对侧),所以我们只是将它们连接起来以获得我们想要的值。
答案 1 :(得分:7)
如果您正在寻找问题中给出的具体示例的答案,或者为什么它错误地使用?
(无论语法如何),请参阅the answer by Laurence Gonsalves。
如果你正在寻找一般问题的答案,为什么?
没有像你期望的那样在sed中表现出它的特殊含义:
默认情况下,sed使用“POSIX基本正则表达式语法”,因此必须将问号转义为\?
以应用其特殊含义,否则它将与文字问号匹配。作为替代方案,您可以使用-r
或--regexp-extended
选项来使用“扩展正则表达式语法”,该语法会反转转义和非转义特殊字符(包括?
)的含义。
用GNU sed文档的话来说(在Linux上运行'info sed'查看):
基本和扩展正则表达式之间的唯一区别在于 几个字符的行为:'?','+',括号和大括号 ( '{}')。虽然基本的正则表达式要求转义这些,如果 你希望它们在使用扩展时表现为特殊字符 正则表达式,如果你希望它们匹配a,你必须转义它们 文字字符。
并解释了该选项:
-r
--regexp-extended
使用扩展正则表达式而不是基本常规表达式 表达式。扩展的正则表达式是“egrep”接受的那些; 它们可以更清晰,因为它们通常具有较少的反斜面, 但是是GNU扩展,因此使用它们的脚本不是 便携式的。
<强>更新强>
更新版本的GNU sed现在说:
-E
-r
--regexp-extended
使用扩展正则表达式而不是基本常规表达式 表达式。扩展的正则表达式是'egrep'接受的;他们 可以更清楚,因为它们通常有较少的反斜杠。 从历史上看,这是一个GNU扩展,但'-E'扩展有 自从被添加到POSIX标准以来 (http://austingroupbugs.net/view.php?id=528),所以请使用'-E' 可移植性。 GNU sed已接受'-E'作为无证选项 多年来,* BSD seds也接受了'-E'多年,但是 使用'-E'的脚本可能不会移植到其他旧系统。
因此,如果您需要保持与古老GNU sed的兼容性,请坚持使用-r
。但如果您希望在更现代的系统上更好的跨平台可移植性(例如Linux + Mac支持),请使用-E
(但请注意,GNU sed和BSD sed之间仍存在一些怪癖和差异,所以你会必须确保你的脚本在任何情况下都是可移植的。)
答案 2 :(得分:1)
echo 'file_1.gz'|sed -n 's/.*_\(.*\)\?\(\.gz\)/\1/p'
作品。你必须把回报放在正确的位置,你必须逃脱它。
答案 3 :(得分:0)
在字段抓取/解析时,您应该使用优于awk
的{{1}}:
sed
或者你可以像这样使用Bash的参数扩展:
$ awk -F'[._]' '{print $2}' <<<"file_1"
1
$ awk -F'[._]' '{print $2}' <<<"file_1.gz"
1
注意:同时适用于 var=file_1.gz;
temp=${var#*_};
file=${temp%.*}
echo $file
答案 4 :(得分:0)
一个函数,它应返回文件名中“_”后面的数字,而不管文件扩展名是什么:
realname () {
local n=${$1##*/}
local rn="${n%.*}"
sed 's/^.*\_//g' ${$rn:-$n}
}
答案 5 :(得分:0)
解决方案的一部分在于转义问号或使用-r
选项。
sed 's/.*_\([^.]*\)\(\.\?[^.]\+\)\?$/\1/'
或
sed -r 's/.*_([^.]*)(\.?[^.]+)?$/\1/'
适用于:
file_1.gz
file_12.txt
file_123
导致:
1
12
123
答案 6 :(得分:0)
我刚才意识到可以做一些非常简单的事情:
echo 'file_1.gz'|sed -n 's/.*_\([0-9]*\).*/\1/p'
请注意[0-9]*
而不是.*
。 @Laurence Gonsalves的回答让我意识到我之前帖子的贪婪。