ChatGPT解决这个技术问题 Extra ChatGPT

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

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

m
mklement0

更新:从 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 应该报告 20200816 或更多最近的):

# 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

以下适用于直到 Catalina (10.15) 的 macOS:

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

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

BSD sedawk 将它们的功能大部分限制在 POSIX sedPOSIX awk 规范要求的范围内,而它们的 GNU 对应物实现了更多的扩展。


修复语言环境:blogs.agilefaqs.com/2014/01/12/…
m
mklement0

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

大写“我”。

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 否则它只会在第一场比赛中运行。
R
Remi Guan

Mac OS X 上 sed 的另一个解决方法是从 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
B
Benjamin W.

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

/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。
B
Benjamin W.

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 这与答案中的任何内容都没有矛盾,对吧?还是您想通过链接到官方规范来添加上下文?我可以将其添加到答案中。
这里没有什么矛盾的。我很高兴看到有人引用 POSIX 并想添加一个链接。这里的大多数答案都忙于哀叹 sed 的“非标准”macOS 实现,这让我感到困扰。
@D.Shawley 现在添加了规范的链接 :)
A
Alastair Irvine

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

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

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

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

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

g
gojimmypi

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

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

grep -i -l -r foo ./* 

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

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

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


D
Deb

以下应该没问题:

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