我想对目录层次结构中的文件执行不区分重音的搜索。
$ touch a ą ä à á â
$ find . -iname '*a*'
./a
# How do I get find to return all 6 filenames?
我正在运行 Debian 11,Bullseye
我的校对符很弱!
是否有一个选项、区域设置或其他一些方法可以让我以不区分重音的方式进行查找工作?
根据评论中的要求,locale
返回:
LANG=en_GB.UTF-8
LANGUAGE=en_GB:en
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=
答案1
TL;DR 滚动到最后
这是一个很棒的问题。谢谢你的提问。
据我所知,可以执行不区分重音的搜索,但默认情况下不会,也不会自动执行。您可以使用以下命令找到所有六个示例文件:
find . -name '[[=a=]]'
这是标准 POSIX glob 表示法,用于表示所有类似但可能带有重音的字符。
因此,如果您知道所有可能有重音版本的字符,则可以使用上述表示法明确地在您的搜索中。例如:
find . -name 'fran[[=c=]]ais' # To match a cedilla
但这是乏味的并且非常令人不满意。
请注意,该[[=a=]]
表示法也可以用于没有任何重音版本的字符。所以[[=k=]]
会匹配k
。
所以我建议创建一个脚本(带口音的),它在命令行上获取一个字符串,用[[=x=]]
它的版本替换每个字母,并打印出结果,然后您可以将其与寻找。例如:
#!/usr/bin/env perl
print join('', map { /\p{Letter}/ ? "[[=$_=]]" : $_ } split //, $ARGV[0]), "\n";
使用它与寻找可能看起来像:
find . -name "`accented a`"
如果你想让它感觉自动,并且如果你只使用寻找以最简单的方式,您可以创建一个 shell 脚本(芬德)结合了寻找和带口音的:
#!/bin/sh
find "$1" -name "`accented \"$2\"`"
然后你可以这样做:
ffind . a
但这将导致无法使用寻找的其他谓词。
当你需要它时,你必须使用真正的寻找和带口音的明确地(如上面)。
这里
更聪明的解决方案是一个包装器寻找(芬德)扫描-name
和-iname
参数,并有效地应用带口音的到以下参数,然后执行修改后的结果寻找命令。例如:
#!/usr/bin/env perl
use warnings;
use strict;
# ffind - find wrapper that makes -name and -iname accent-insensitive
my @cmd;
while (@ARGV)
{
# Gather command line arguments
push @cmd, shift @ARGV;
# Make -name and -iname arguments accent-insensitive
if ($cmd[-1] =~ /^-i?name$/ && @ARGV)
{
push @cmd, join('', map { /\p{Letter}/ ? "[[=$_=]]" : $_ } split //, shift @ARGV);
}
}
exec 'find', @cmd;
然后您可以这样做来查找所有六个示例文件:
ffind . -name a
当然,你也可以称之为寻找'find'
并将最后一行的 更改为'/usr/bin/find'
,这将使寻找透明地不区分重音:
find . -name a
遗憾的是,这整个方法仅适用于某些系统,例如 Debian 12,但并非全部。 :-(
答案2
您可以将名称转换为分解形式,删除组合标记并进行检查:
find . -print0 |
perl -C -MUnicode::Normalize -MFile::Basename -0 -lne '
$name = NFD(basename($_)) =~ s/\pM//r;
print if $name =~ /a/' |
xargs -r0 ls -ld --