我正在尝试重命名包含字符“à”的文件。
我执行以下操作:
rename -v 's/à/a/g' *
但它显示所有文件都未更改。详细模式显示了同样的事情。
我试图逃跑,\
但没有成功。
如何使正则表达式匹配这种类型的字符?
编辑
的输出perl -V
:
Summary of my perl5 (revision 5 version 18 subversion 2) configuration:
Platform:
osname=darwin, osvers=16.0, archname=darwin-thread-multi-2level
uname='darwin osx320.apple.com 16.0 darwin kernel version 15.0.0: wed jun 22 17:57:08 pdt 2016; root:xnu-3247.1.106.2.9~1development_x86_64 x86_64 '
config_args='-ds -e -Dprefix=/usr -Dccflags=-g -pipe -Dldflags= -Dman3ext=3pm -Duseithreads -Duseshrplib -Dinc_version_list=none -Dcc=cc'
hint=recommended, useposix=true, d_sigaction=define
useithreads=define, usemultiplicity=define
useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
use64bitint=define, use64bitall=define, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='cc', ccflags ='-arch x86_64 -arch i386 -g -pipe -fno-common -DPERL_DARWIN -fno-strict-aliasing -fstack-protector',
optimize='-Os',
cppflags='-g -pipe -fno-common -DPERL_DARWIN -fno-strict-aliasing -fstack-protector'
ccversion='', gccversion='4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)', gccosandvers=''
intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define
Linker and Libraries:
ld='cc -mmacosx-version-min=10.12.5', ldflags ='-arch x86_64 -arch i386 -fstack-protector'
libpth=/usr/lib /usr/local/lib
libs=
perllibs=
libc=, so=dylib, useshrplib=true, libperl=libperl.dylib
gnulibc_version=''
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
cccdlflags=' ', lddlflags='-arch x86_64 -arch i386 -bundle -undefined dynamic_lookup -fstack-protector'
Characteristics of this binary (from libperl):
Compile-time options: HAS_TIMES MULTIPLICITY PERLIO_LAYERS
PERL_DONT_CREATE_GVSV
PERL_HASH_FUNC_ONE_AT_A_TIME_HARD
PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP
PERL_PRESERVE_IVUV PERL_SAWAMPERSAND USE_64_BIT_ALL
USE_64_BIT_INT USE_ITHREADS USE_LARGE_FILES
USE_LOCALE USE_LOCALE_COLLATE USE_LOCALE_CTYPE
USE_LOCALE_NUMERIC USE_PERLIO USE_PERL_ATOF
USE_REENTRANT_API
Locally applied patches:
/Library/Perl/Updates/<version> comes before system perl directories
installprivlib and installarchlib points to the Updates directory
Built under darwin
Compiled at Feb 6 2017 22:16:22
@INC:
/Library/Perl/5.18/darwin-thread-multi-2level
/Library/Perl/5.18
/Network/Library/Perl/5.18/darwin-thread-multi-2level
/Network/Library/Perl/5.18
/Library/Perl/Updates/5.18.2
/System/Library/Perl/5.18/darwin-thread-multi-2level
/System/Library/Perl/5.18
/System/Library/Perl/Extras/5.18/darwin-thread-multi-2level
/System/Library/Perl/Extras/5.18
.
编辑2:
输出locale
:
LANG=
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=
解决方案
简而言之,这就是有效的方法。所有 3 个解决方案都发挥了作用:
rename -nv $'s/a\xcc\x80/a/g' *
PERL_UNICODE=AS rename -n 's/\pM//g' ./*
。 (参见所选答案中的解释)- 切换到
zsh
,而不是 MacOS 的默认 Shell (bash
),那么我原来的命令(无需指定组合字符,例如a\u300
)就可以工作:rename -v 's/à/a/g' *
。
如果您对这些解决方案都不满意,请查看所选答案以查找有用的提示。
答案1
在 macOS 和至少 HFS+ 文件系统上,重音字符以其分解形式进行编码,因此à
编码为a\u300
(a
后跟结合严肃的口音 组合字符)即使您使用touch $'\ue0'
(预先组合的形式(a
带有重音的独立形式)创建了文件,也会导致各种错误(以及莱纳斯·托瓦尔德著名的咆哮之一)喜欢它的伪大小写不敏感。
您会注意到,如果您这样做:
touch à; echo ?
要列出由一个字符组成的文件名,它不会返回任何内容:
echo ??
或者
echo *a*
确实返回该值à
(实际上à
)。和:
$ echo ?? | uconv -x name
\N{LATIN SMALL LETTER A}\N{COMBINING GRAVE ACCENT}\N{<control-000A>}
所以你需要:
rename $'s/a\u300/a/g' ./*
(假设zsh
或兼容的外壳)。或者手动指定 U+0300 字符 (0xcc 0x80) 的 UTF-8 编码,对于支持 ksh93$'...'
引号但不支持zsh
's 的shell(如macOS 上的$'\u300'
古老版本):bash
rename $'s/a\xcc\x80/a/g' ./*
或者直接perl
解释这些\xcc\x80
序列:
rename 's/a\xcc\x80/a/g' ./*
或者 unicode 字符:
PERL_UNICODE=AS rename 's/\x{300}//' ./*
或者删除所有组合字符:
PERL_UNICODE=AS rename -n 's/\pM//g' ./*
在那里,我们告诉perl
要考虑A
参数和S
tdio 流以 UTF-8 编码(请参阅相当于该选项的环境变量perldoc perlrun
的描述),并删除所有具有ark Unicode属性的字符(是or的缩写,请参阅详情)$PERL_UNICODE
-C
M
p
\pM
\p{Mark}
\p{Combining_Mark}
perldoc perluniprops
请注意,您应该能够zsh
通过以下方式列出该文件(在 中):
ls -d $'a\u300'
和:
ls -d $'\ue0'
($'A\u300' and possibly $'\uc0
因为À
它不区分大小写),但是:
ls -d *A*
以及除以下之外的外壳zsh
:
ls -d *$'\ue0'*
ls -d *$'\xc3\xa0'*
不会匹配它,因为 shell 列出当前目录的内容并对每个文件名应用模式,并且文件名被编码为a\u300
不匹配。
但是,仅在zsh
macOS 上,shell 会在内部将这些具有组合重音符号的字母转换为其预组合形式,readdir()
就像将它们传递给iconv -f UTF-8-MAC -t UTF-8
.它自己的内部zreaddir()
包装readdir()
确实返回 U+00E0 而不是aU+0300
解释为什么echo *à*
在那里工作(而不是echo *a*
)而不是在其他地方工作。
该变更于 2014 年 6 月推出。请参阅有关更多详细信息,请在 zsh 邮件列表上进行讨论。
问题的核心在于用户输入所使用的编码与文件系统中用于存储(和列出)文件名的编码之间的差异。韩语中的问题要严重得多,几乎每个字符都有预先组合和分解的形式,这解释了为什么 zsh 问题最初是由韩国人提出的。
所以zsh
基本上修复苹果在文件系统中对分解形式的选择很差,因此可以使用它的完成和通配符,但不幸的是,这仅适用于zsh
,ls | grep à
否则find . -name '*à*'
仍然不起作用。