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

Posted

技术标签:

【中文标题】不区分大小写的搜索和替换为 sed【英文标题】:Case-insensitive search and replace with sed 【发布时间】:2011-05-23 17:07:07 【问题描述】:

我正在尝试使用 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'

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

【问题讨论】:

您可以尝试更新您的 sed 副本吗? I 是一个 GNU 扩展,您的 sed 副本可能不提供它。 EDIT:我通过了 OS X 资格,因为 OP 接受了一个在 OS X 上不起作用的答案。(正如另一个答案所示,sed 在 OS X不支持不区分大小写的匹配,这与 Apple 文档相反。) @danorton:谢谢你;如果您从以下我的回答中得出 Apple 文档承诺实现未提供的某些东西的感觉:man sed 与实现一致 - 没有提及(并且在实践中不支持)不区分大小写的匹配;如果您发现有其他声明的文件,请告知我们。 @mklement0,是的,对不起,我的立场是正确的。 Apple 文档没有对 sed 的不区分大小写匹配做出任何声明。 FWIW,BSD 版本随 OS X 一起提供的工具的 GNU 版本可从各种包管理器中获得。我通过Homebrew 安装了全套文本实用程序,并带有g 前缀,因此当我需要库存版本中没有的功能时,我可以使用gsedgdate 【参考方案1】:

使用以下替换所有匹配项:

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

【讨论】:

见***.com/a/4412964/4294399,它涵盖了首都I。我也不认为这真的回答了这个问题,因为它没有询问全局替换。【参考方案2】:

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

但是要在POSIX sed 中执行此操作,他们建议了三个选项(此处适用于替换):

    转换为大写并将原始行存储在保持空间中;但是,这不适用于替换,因为原始内容将在打印之前恢复,因此它仅适用于基于不区分大小写的匹配插入或添加行。

    可能的可能性仅限于FOOFoofoo。这些可以通过

     s/FOO/bar/;s/[Ff]oo/bar/
    

    要搜索所有可能的匹配项,可以对每个字符使用括号表达式:

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

【讨论】:

pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html 是您可以在 sed 中轻松完成的操作 @D.Shawley 这与答案中的任何内容都没有矛盾,对吧?还是您想通过链接到官方规范来添加上下文?我可以将其添加到答案中。 W 这里没有什么矛盾的。我很高兴看到有人引用 POSIX 并想添加一个链接。这里的大多数答案都在忙着抱怨 sed 的“非标准”macOS 实现,这让我感到困扰。 @D.Shawley 现在添加了规范的链接 :)【参考方案3】:

更新:从 ma​​cOS Big Sur (11.0) 开始,sed 现在是否支持 I 标志不区分大小写的匹配,因此问题中的命令现在应该可以工作(BSD sed 不报告其版本,但您可以按man 页面底部的日期,这应该是March 27, 2017 或更新);一个简单的例子:

# BSD sed on macOS Big Sur and above (and GNU sed, the default on Linux)
$ sed 's/ö/@/I' <<<'FÖO'
F@O   # `I` matched the uppercase Ö correctly against its lowercase counterpart

注意:I(大写)是标志的记录形式,但i 也可以。

同样,从 ma​​cOS Big Sur (11.0) awk 现在 支持区域设置awk --version 应该报告 @987654336 @ 或更新):

# BSD awk on macOS Big Sur and above (and GNU awk, the default on Linux)
$ awk 'tolower($0)' <<<'FÖO'
föo  # non-ASCII character Ö was properly lowercased

以下内容适用于 ma​​cOSCatalina (10.15)

明确一点:在 macOS 上,sed - 这是 BSD 实现 - 不支持不区分大小写的匹配 - 难以置信,但确实如此。 formerly accepted answer 本身显示了一个 GNU sed 命令,由于 cmets 中提到的基于 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 也不是一个选项,就像 macOS 上的 awk(即,BWK awkBSD awk) 似乎完全不知道语言环境 - 它的 tolower()toupper() 函数忽略外来字符(并且 sub() / gsub() 没有以不区分大小写的标志开头)。)


关于sedawk 与 POSIX 标准的关系的注释:

BSD sedawk 将它们的功能大部分限制在 POSIX sed 和 POSIX awk 规范要求,而他们的 GNU 同行实现了更多的扩展。

【讨论】:

修复语言环境:blogs.agilefaqs.com/2014/01/12/…【参考方案4】:

编者注:此解决方案不适用于 macOS(开箱即用),因为它仅适用于 GNU sed,而 macOS BSD 自带sed.

将“I”大写。

sed 's/foo/bar/I' file

【讨论】:

我也看到了这个,并尝试过......但我仍然收到相同的错误消息。 BSD sed 似乎有很多限制。如果是这样的话,我会在 PERL 中执行此操作(即 perl -pe 's/foo/bar/i')。 OS X Lion 的默认安装给出了错误:sed: 1: "s/foo/bar/I": bad flag in substitution command: 'I' I 后缀不是sed 的可移植使用。 POSIX sed 仅使用 Basic Regular Expressions (BREs),这是非常有限的。他们甚至不支持+(您必须改用\1,\),更不用说不区分大小写的匹配了。使用 sed 进行此操作的唯一可移植方法是检查 /[hH][eE][lL][lL][oO]/ 之类的内容,这通常是不切实际的。 那必须是/gI 否则它只会在第一场比赛中运行。【参考方案5】:

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

/pattern/s/xx/yy/g

那么你想把I放在模式之后:

/pattern/Is/xx/yy/g

例子:

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

返回willma;如果没有I,它会返回未触及的字符串(Fred)。

【讨论】:

在 MacO 上我得到:sed: 1: "/fred/Is//willma/g": invalid command code I 好提示。以下是我在复杂搜索中使用它的方法:sed -r '/'"$PATTERN"'/I,$s//'$YELLOW'&amp;'$NO_COLOR'/g;b;$q3'。它打印文本,如果找到模式(不区分大小写),它会以黄色(ansi 颜色)突出显示文本。如果未找到 - 返回退出代码 3。【参考方案6】:

我也有类似的需求,想出了这个:

这个命令可以简单地查找所有文件:

grep -i -l -r foo ./* 

this 排除 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 结果只允许查看带有目标文本的文件(因此也可能会提高性能/速度)

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

【讨论】:

【参考方案7】:

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

【讨论】:

gsed "s/a/b/Ig" 有效,谢谢!为什么一个好的工作答案会被否决? 这个答案很棒。使用 brew install gnu-sed 然后转到我的 ~/.bash_profile 并添加别名。谢谢@davmat 最好是brew install gnu-sed --with-default-names - 这将覆盖默认的sed @Mar0ux --with-default-names 现在已弃用:brew.sh 我将 gnu-sed 添加到我的 PATH,但我相信现在还有其他解决方法:SE question【参考方案8】:

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

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

【讨论】:

这是一件特别令人发指的事情。如果有人认真考虑这一点,只需在本地安装 GNU sed。 矫枉过正但有用的通用方法知道!

以上是关于不区分大小写的搜索和替换为 sed的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL 2000/2005 中执行区分大小写的搜索和替换?

使用 sed 将唯一文本替换为 URL

sed

sed

如何将 Presto 搜索配置为不区分大小写?

是否存在不区分大小写的 string.Replace 替代方法?