我使用 GNU grep 3.4 来查找包含特定模式的脚本。为此,我grep
像这样递归调用
grep -rin . -e "pattern"
模式只是一个单词,不是正则表达式。奇怪的是,输出没有列出某些文件中肯定包含该字符串的出现情况。
我尝试打开这些文件vim
并使用进行搜索/pattern
,它找到了模式。编码显示为vim
。[dos:utf-8:]
当我复制该行并将其写入新文件时,上述grep
命令会正确列出它。
为什么不grep
列出原始文件?
答案1
Grep(或者至少是较旧的版本)不理解 UTF8。因此,组合字符、连字符点或其他不可见数据可能会阻止 grep。
Grep 也受到 $LC_ALL、$LC_CTYPE 和 $LANG 的值的影响。
使用 vim 保存 grep 无法找到的单词周围的几行,然后对该小示例文件进行十六进制转储。您可能会明白 grep 失败的原因
您也可以使用 vim 命令(ga
等g8
)来检查字符,但十六进制转储可能更清晰
答案2
我找到了问题(在另一个答案的帮助下)。文件“grep”没有显示任何输出,但实际上并未utf-8
编码utf-16be
。我使用 hexdump 了解到了这一点(感谢@RedGrittyBrick):
hd file_for_which_grep_works_as_expected.txt
产量
00000000 20 20 20 20 50 61 74 74 65 72 6e 0a | Pattern.|
0000000c
然而
hd file_for_which_grep_fails.txt
回
00000000 fe ff 00 50 00 61 00 74 00 74 00 65 00 72 00 6e |...P.a.t.t.e.r.n|
00000010 00 0a |..|
00000012
因此,使用以下方法仔细检查编码
file -i file_for_which_grep_fails.txt
将其标识为text/plain; charset=utf-16be
。
我没有意识到所utf-8
展示的vim
实际上是缓冲编码,而不是文件编码。执行:set fileencoding
时vim
也正确显示fileencoding=utf-16
(在此处找到https://superuser.com/a/28783/1210682)。
因此,问题在于我的代码grep
无法处理utf-16
编码文件。这里已经对此进行了描述:https://superuser.com/a/231471/1210682utf-16
。但是,当我以递归方式使用将文件转换为utf-8
之前的补救措施grep
时,它不起作用,因为我事先不知道哪些文件可能是utf-8
,哪些是,utf-16
并且正在搜索大量文件。
有不同的解决方案,我将在这里简要介绍其中两种:
A快速而粗略的对我有用的解决方案是扩展搜索模式以包含与
utf-16
版本匹配的搜索模式并搜索两种模式之一:grep -riPa . -e "pattern|p.a.t.t.e.r.n."
就可能的模式而言,这当然是非常有限的。
grep
还有其他类似ugrep
或ripgrep
可以处理文件的程序utf-16
。我最终使用了ripgrep
从 18.04 开始的标准 Ubuntu 软件包存储库中提供的程序:rg -i "pattern"
这里对替代方案进行了很好的讨论:https://stackoverflow.com/questions/3752913/grepping-binary-files-and-utf16其中一种有趣的方法是尝试将搜索模式转换为utf-16
并将其输入到grep
。但是,我无法让它工作。