不区分大小写的搜索和替换为 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
前缀,因此当我需要库存版本中没有的功能时,我可以使用gsed
或gdate
。
【参考方案1】:
使用以下替换所有匹配项:
sed 's/foo/bar/gI' mylog.txt
【讨论】:
见***.com/a/4412964/4294399,它涵盖了首都I
。我也不认为这真的回答了这个问题,因为它没有询问全局替换。【参考方案2】:
sed FAQ 处理密切相关的不区分大小写的搜索。它指出 a) 许多版本的 sed 都支持它的标志 b) 在 sed 中这样做很尴尬,您应该使用 awk 或 Perl。
但是要在POSIX sed 中执行此操作,他们建议了三个选项(此处适用于替换):
转换为大写并将原始行存储在保持空间中;但是,这不适用于替换,因为原始内容将在打印之前恢复,因此它仅适用于基于不区分大小写的匹配插入或添加行。
可能的可能性仅限于FOO
、Foo
和foo
。这些可以通过
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】:更新:从 macOS 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
也可以。
同样,从 macOS 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
以下内容适用于 macOSCatalina (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 awk 和 BSD awk) 似乎完全不知道语言环境 - 它的 tolower()
和 toupper()
函数忽略外来字符(并且 sub()
/ gsub()
没有以不区分大小写的标志开头)。)
关于sed
和awk
与 POSIX 标准的关系的注释:
BSD sed
和 awk
将它们的功能大部分限制在 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'&'$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的主要内容,如果未能解决你的问题,请参考以下文章