用?与sed

时间:2010-12-03 17:30:49

标签: linux bash sed

我只想获取一个可能是也可能不是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为1file_1提供file_1.gz。如果执行时间至关重要,那么在bash脚本中执行此操作的最佳方法是什么?

7 个答案:

答案 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的回答让我意识到我之前帖子的贪婪。