不区分大小写的搜索并替换为sed


81

我正在尝试使用SED从日志文件中提取文本。我可以轻松进行搜索和替换:

sed 's/foo/bar/' mylog.txt

但是,我想使搜索不区分大小写。从我搜索过的内容来看,似乎i可以在命令末尾附加内容:

sed 's/foo/bar/i' mylog.txt

但是,这给了我一条错误消息:

sed: 1: "s/foo/bar/i": bad flag in substitute command: 'i'

这里出了什么问题,我该如何解决?


2
您可以尝试更新sed的副本吗?I是GNU扩展,可能与您的sed副本不可用。
Lazer 2010年

4
编辑:我突破了OS X的资格要求,因为OP接受了在OS X上不起作用的答案。(如另一个答案所示,在OS X上的sed不支持不区分大小写的匹配,与Apple文档相反。)
danorton

1
@danorton:谢谢你。如果您从以下我的回答中得出Apple文档承诺实现未实现的某些含义:man sed与实现保持一致-不提及(并且在实践中不提供支持)不区分大小写的匹配;如果您发现有其他要求的文档,请告知我们。
mklement0 2014年

1
@ mklement0,是的,很抱歉,我已经纠正了。Apple文档没有要求对sed区分大小写。
danorton 2014年

1
FWIW是OS X随附BSD版本的工具的GNU版本,可以从各种程序包管理器中获得。我通过Homebrew安装了带有g前缀的全套文本实用程序,因此可以使用gsedgdate在需要通用版本中未提供的功能时使用。
马克·里德

Answers:


72

需要明确的是:在macOS上-自Mojave(10.14)起--sed这是BSD实现-不支持不区分大小写的匹配-难以置信,但事实如此。在以前接受的答案,这本身就说明一个GNU sed命令,获得因为这种地位perl在评论中提到基于解决方案。

要使该Perl解决方案也可以通过UTF-8与外来字符一起使用,请使用以下方法:

perl -C -Mutf8 -pe 's/öœ/oo/i' <<< "FÖŒ" # -> "Foo"
  • -C 假设当前语言环境基于UTF-8,则打开对流和文件的UTF-8支持。
  • -Mutf8告诉Perl将源代码解释为UTF-8(在这种情况下,将字符串传递给-pe)-这是更冗长的等价形式的较短等效词,-e 'use utf8;'.谢谢,Mark Reed

(请注意,使用awk也不是一个选项,例如awk在macOS上(即BWK awk,又名BSD awk)似乎完全不知道语言环境-它tolower()toupper()函数会忽略外来字符(并且sub()/gsub()对大小写不区分大小写)首先)。)



69

编者注:该解决方案不适用于macOS(开箱即用),因为它仅适用于GNU sed,而macOS随附BSD sed

大写“ I”。

sed 's/foo/bar/I' file

2
我也看到了,并尝试了...但是我仍然收到相同的错误消息。
Craig Walker 2010年

15
看来BSD sed有很多限制。如果是这种情况,我会在PERL中执行此操作(即perl -pe's / foo / bar / i')。
韦斯利·赖斯2010年

3
OS X Lion的默认安装给出错误:sed:1:“ s / foo / bar / I”:替换命令中的错误标志:“ I”
Ben Clayton'4

13
I后缀不是一个便携使用的sed。POSIXsed仅使用基本正则表达式(BRE),这令人惊讶地受到限制。他们甚至不支持+(您必须使用\{1,\}),更不用说不区分大小写的匹配了。使用sed进行操作的唯一便携式方法是检查类似之类的东西/[hH][eE][lL][lL][oO]/,这通常是不切实际的。

5
需要被/gIothewise它只是在第一场比赛操作。
Faheem Mitha 2014年

25

sed在Mac OS X上,另一个解决方法是gsed从MacPorts或HomeBrew安装,然后创建别名sed='gsed'


gsed的“ s / a / b / Ig”有效,谢谢!为什么一个好的工作答案会被否决?
Matthias M

3
这个答案很好。brew install gnu-sed然后使用我的〜/ .bash_profile并添加别名。感谢@davmat
ThinkBonobo 2015年

8
最好这样做brew install gnu-sed --with-default-names-这将覆盖默认值sed
Mar0ux

4

sed的FAQ地址密切相关的不区分大小写的搜索。它指出:a)许多版本的sed支持它的标志,并且b)在sed中这样做很尴尬,您应该使用awk或Perl。

但是要在POSIX sed中做到这一点,他们建议了三种选择(此处适合替代):

  1. 转换为大写并将原始行存储在保留空间中;但是,这对于替换无效,因为原始内容将在打印前恢复,因此仅适用于基于大小写不敏感的匹配插入或添加行。

  2. 也许可能仅限于FOOFoofoo。这些可以由

    s/FOO/bar/;s/[Ff]oo/bar/
    
  3. 要搜索所有可能的匹配项,可以为每个字符使用方括号表达式:

    s/[Ff][Oo][Oo]/bar/
    

1

Mac版本sed似乎有点受限制。解决此问题的一种方法是使用Linux容器(通过Docker),该容器的可用版本为sed

cat your_file.txt | docker run -i busybox /bin/sed -r 's/[0-9]{4}/****/Ig'

15
这是一件特别令人发指的事情。如果有人甚至认真考虑这一点,只需在本地安装GNU sed。
ocodo

矫kill过正,但有用的一般方法要知道!
YvesgereY

1

如果您要先进行模式匹配,例如

/pattern/s/xx/yy/g

那么您想I在模式之后放置:

/pattern/Is/xx/yy/g

例:

echo Fred | sed '/fred/Is//willma/g'

回报willma; 如果不使用I,则返回未修改的字符串(Fred)。


2
在MacO上,我得到:sed: 1: "/fred/Is//willma/g": invalid command code I
Chris F Carroll '18

好提示。这是我在复杂搜索中使用它的方式:sed -r '/'"$PATTERN"'/I,${s//'$YELLOW'&'$NO_COLOR'/g;b};$q3'。它会打印文本,如果找到了模式(不区分大小写),则会以黄色(ansi颜色)突出显示文本。如果没有找到-返回退出代码3
诺姆·马诺斯

0

我也有类似的需求,并提出了以下建议:

此命令仅查找所有文件:

grep -i -l -r foo ./* 

排除this_shell.sh(如果您将命令放在名为this_shell.sh的脚本中),将输出发到控制台以查看发生了什么,然后在找到的每个文件名上使用sed,用bar替换文本foo :

grep -i -l -r --exclude "this_shell.sh" foo ./* | tee  /dev/fd/2 | while read -r x; do sed -b -i 's/foo/bar/gi' "$x"; done 

我选择了这种方法,因为我不希望为未修改的文件更改所有时间戳。提供grep结果仅允许查看带有目标文本的文件(因此也可能会提高性能/速度)

使用前请务必备份文件并进行测试。在某些环境中,对于带有嵌入式空间的文件可能不起作用。(?)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.