我正在尝试使用 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'
这里出了什么问题,我该如何解决?
I
是一个 GNU 扩展,您的 sed 副本可能不提供它。
man sed
与实现一致 - 没有提及(并且在实践中不支持)不区分大小写的匹配;如果您发现一份文件声称另有说明,请告知我们。
g
前缀的 Homebrew 安装了全套文本实用程序,因此当我需要库存版本中没有的功能时,我可以使用 gsed
或 gdate
。
更新:从 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 awk 和 BSD awk)出现完全不知道语言环境 - 它的 tolower()
和 toupper()
函数忽略外来字符(并且 sub()
/ gsub()
没有以不区分大小写的标志开头)。)
关于 sed
和 awk
与 POSIX 标准的关系的注释:
BSD sed
和 awk
将它们的功能大部分限制在 POSIX sed
和 POSIX awk
规范要求的范围内,而它们的 GNU 对应物实现了更多的扩展。
编者注:此解决方案不适用于 macOS(开箱即用),因为它仅适用于 GNU sed
,而 macOS 带有 < em>BSD sed
.
大写“我”。
sed 's/foo/bar/I' file
I
后缀不是 sed
的可移植使用。 POSIX sed
仅使用 Basic Regular Expressions (BREs),这是非常有限的。它们甚至不支持 +
(您必须改用 \{1,\}
),更不用说不区分大小写的匹配了。使用 sed 进行此操作的唯一可移植方法是检查 /[hH][eE][lL][lL][oO]/
之类的内容,这通常是不切实际的。
/gI
否则它只会在第一场比赛中运行。
Mac OS X 上 sed
的另一个解决方法是从 MacPorts 或 HomeBrew 安装 gsed
,然后创建别名 sed='gsed'
。
brew install gnu-sed
然后转到我的 ~/.bash_profile 并添加别名。谢谢@davmat
brew install gnu-sed --with-default-names
- 这将覆盖默认的 sed
。
如果您首先进行模式匹配,例如,
/pattern/s/xx/yy/g
那么你想把 I
放在模式之后:
/pattern/Is/xx/yy/g
例子:
echo Fred | sed '/fred/Is//willma/g'
返回willma
;如果没有 I
,它将返回未触及的字符串 (Fred
)。
sed: 1: "/fred/Is//willma/g": invalid command code I
sed -r '/'"$PATTERN"'/I,${s//'$YELLOW'&'$NO_COLOR'/g;b};$q3'
。它打印文本,如果找到模式(不区分大小写),它会以黄色(ansi 颜色)突出显示文本。如果未找到 - 返回退出代码 3。
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 /
sed
的 Mac 版本似乎有点受限。解决此问题的一种方法是使用具有可用版本 sed
的 linux 容器(通过 Docker):
cat your_file.txt | docker run -i busybox /bin/sed -r 's/[0-9]{4}/****/Ig'
使用以下替换所有匹配项:
sed 's/foo/bar/gI' mylog.txt
我有类似的需求,并想出了这个:
此命令可以简单地查找所有文件:
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 结果只允许查看带有目标文本的文件(因此也可能会提高性能/速度)
请务必在使用前备份您的文件并进行测试。对于带有嵌入空格的文件,在某些环境中可能不起作用。 (?)
以下应该没问题:
sed -i 's/foo/bar/gi' mylog.txt