在sed命令行中我需要引用什么?

时间:2013-09-12 08:41:08

标签: regex bash sed sh

关于如何逃避sed的各种元素,这个网站上有很多问题,但我正在寻找更一般的答案。我知道我可能想要逃避一些字符以避免shell扩展:

  

Bash

     
      
  • 单引号[strings] ('')用于保留引号中包含的每个字符的文字值。 [但是,]单引号之间可能不会出现 单引号 ,即使前面有反斜杠也是如此。
  •   
  • 只有当后跟美元反引号双引号时,反斜杠才会保留[在双引号字符串]中的含义strong>,反斜杠换行符。在双引号内,当后跟其中一个字符时,反斜杠将从输入流中删除。不具有特殊含义的字符前面的反斜杠不会被修改以供shell解释器处理。
  •   
     

sh :(我希望你没有历史扩展)

     
      
  • 单引号字符串行为:与bash相同
  •   
  • 用双引号括起字符可保留字面值      引号中的所有字符,美元单引号反斜杠除外,以及      启用历史记录扩展时,感叹号。   
        
    • 字符dollar单引号在双引号中保留其特殊含义。
    •   
    • 反斜杠仅在后跟以下字符之一时保留其特殊含义:$'"\或换行符。 双引号可以在双倍范围内引用   引用前面的反斜杠。
    •   
    • 如果启用,则会执行历史记录扩展,除非使用反斜杠转义出现在双引号中的感叹号。之前的反斜杠!是已删除。
    •   
  •   

...但是没有一个能解释为什么一旦你删除任何转义就停止工作:

sed -e "s#\(\w\+\) #\1\/#g" #find a sequence of characters in a line
#    why? ↑   ↑ ↑     ↑     #replace the following space with a slash.

()/+(或[] ...)似乎没有特殊意义,要求他们逃脱才能工作。地狱,即使直接通过Python调用命令也会使sed无法正常工作,尽管联机帮助页似乎没有说清楚这一点(不管怎么说,当我搜索反斜杠时)。

$ lvdisplay -C --noheadings -o vg_name,name > test
$ python
>>> import os
>>> #Python requires backslash escaping of \1, even in triple quotes
>>> #lest \1 is read to mean "byte with value 0x01".
>>> output = os.execl("/bin/sed", "-e", "s#(\w+) #\\1/#g", "test")
(Output remains unchanged)
$ python
>>> import os
>>> output = os.execl("/bin/sed", "-e", "s#\(\w\+\) #\\1\/#g", "test")
(Correct output)
$ WHAT THE HELL
Have you tried using jQuery? It's perfect and it does all the things.

4 个答案:

答案 0 :(得分:4)

如果我理解正确,你的问题不是关于bash / sh,而是关于默认使用的正则表达式sed:BRE

  

另一个[=任何点,星号,插入符号和美元] BRE元字符需要反斜杠才能赋予它们特殊的含义。原因是最早版本的UNIX grep不支持这些。

应该对(..)分组进行转义,以赋予其特殊含义。与+相同,否则sed将尝试匹配它们,因为它们是文字字符串/字符。这就是为什么你的s#\(\w\+\) #...#应该被转义的原因。更换部件不需要转义,所以:

sed 's#\(\w\+\) #\1 /#' 

应该有用。

sed通常可以选择使用extended regular expressions(现在使用?+|(){m,n} );例如GNU sed有-r,那么你的单行可能是:

sed -r 's#(\w+) #\1 /#'

我在这里粘贴了一些示例,可以帮助您了解正在发生的事情:

kent$  echo "abcd "|sed 's#\(\w\+\) #\1 /#'
abcd /
kent$  echo "abcd "|sed -r 's#(\w+) #\1 /#'                                                                                                                                 
abcd /
kent$  echo "(abcd+) "|sed 's#(\w*+) #&/#'
(abcd+) /

答案 1 :(得分:1)

您所观察到的是正确的。使用基本时,需要转义某些字符,例如?+(){} strong>正则表达式。

引用sed manual

  

基本和扩展正则表达式之间的唯一区别是   在几个字符的行为:‘?’‘+’,括号和大括号   (‘{}’)。 虽然基本正则表达式要求转义   你希望它们在使用扩展时表现为特殊字符   正则表达式,如果你希望它们匹配a,你必须转义它们   字面意思。

(强调我的。)但是,当使用扩展正则表达式时,不需要对它们进行转义,除非匹配文字字符(如最后一行引用中提到的上面。)

答案 2 :(得分:1)

如果您想要一般性答案,

  • 需要从shell中引用或转义Shell元字符;
  • 如果您需要字面解释,则需要转义正则表达式元字符;
  • 一些正则表达式构造是由反斜杠转义形成的;根据上下文,这些反斜杠可能需要引用。

所以你有以下场景;

# Match a literal question mark
echo '?' | grep \?
# or equivalently
echo '?' | grep "?"
# or equivalently
echo '?' | grep '?'

# Match a literal asterisk
echo '*' | grep \\\*
# or equivalently
echo '*' | grep "\\*"
# or equivalently
echo '*' | grep '\*'

# Match a backreference: any character repeated twice
echo 'aa' | grep \\\(.\\\)\\1
# or equivalently
echo 'aa' | grep "\(.\)\\1"
# or equivalently
echo 'aa' | grep '\(.\)\1'

正如您所看到的,单引号在大多数情况下可能最有意义。

如果要嵌入需要反斜杠引用的语言,则必须添加另一组反斜杠,或者避免调用shell。

正如其他人所指出的那样,扩展正则表达式遵循稍微不同的语法,但一般模式是相同的。最重要的是,为了尽量减少来自shell的干扰,请尽可能使用单引号。

对于文字字符,您可以通过使用字符类来避免一些反斜视。

echo '*' | grep \[\*\]
# or equivalently
echo '*' | grep "[*]"
# or equivalently
echo '*' | grep '[*]'

答案 3 :(得分:-1)

FreeBSD sed,也用于Mac OS X,使用-E代替-r扩展正则表达式。 因此,要使其可移植,请使用基本的正则表达式。例如,在扩展正则表达式模式下,+必须在基本正则表达式模式下用\{1,\}替换。 在基本以及扩展正则表达式模式下,FreeBSD sed似乎无法识别必须用\w替换的[[:alnum:]_](参见man re_format)。

# using FreeBSD sed (on Mac OS X)

# output: Hello, world!
echo 'hello    world' | sed -e 's/h/H/' -e 's/ \{1,\}/, /g' -e 's/\([[:alnum:]_]\{1,\}\)$/\1!/'
echo 'hello    world' | sed -E -e 's/h/H/' -e 's/ +/, /g' -e 's/([[:alnum:]_]+)$/\1!/'
echo 'hello    world' | sed -E -e 's/h/H/' -e 's/ +/, /g' -e 's/(\w+)$/\1!/'  # does not work

# find a sequence of characters in a line
# replace the following space with a slash
# output: abcd+/abcd+/
echo 'abcd+ abcd+ ' > test
python
import os
output = os.execl('/usr/bin/sed', '-e', 's#\([[:alnum:]_+]\{1,\}\) #\\1/#g', 'test')

要将单引号用作sed正则表达式的一部分,同时保留sed正则表达式的外部单引号,可以连接三个单独的字符串,每个字符串用单引号括起来以避免可能的shell扩展

# man bash:
# "A single quote may not occur between single quotes, even when preceded by a backslash."
# cf. http://stackoverflow.com/a/9114512 & http://unix.stackexchange.com/a/82757
# concatenate: 's/doesn'  +  \'  +  't/does not/'
echo "sed doesn't work for me" | sed -e 's/doesn'\''t/does not/'