我正在运行Ubuntu并且echo $ LANG告诉我我使用的是UTF-8:“en_US.UTF-8”。
我创建了一个目录,其中包含一个名为'ö'的文件(德语变音符号)
ronald@lala:~/tempX/test$ ls
ö
我的理解是,由于utf-8编码,文件名由两个字节组成,代表一个字符。因此,我很惊讶这匹配:
ronald@lala:~/tempX/test$ ls | grep "^\W\W$"
ö
ronald@lala:~/tempX/test$ ls | egrep "^\W{2,}$"
ö
ronald@lala:~/tempX/test$ ls | grep -P "^\W{2,}$"
ö
ronald@lala:~/tempX/test$ ls | pcregrep "^\W{2,}$"
ö
为什么grep将'ö'视为两个非单词字符而不只是一个?
祝你好运, 罗纳德
答案 0 :(得分:1)
Grep在字符级别上工作,并考虑当前区域设置的编码和排序规则(在联机帮助页中有记录)。您可以通过切换到C语言环境强制它使用ASCII。
使用pl_PL.UTF-8:
$ echo Ź | grep -i ź
Ź
$ echo ó | grep '[a-z]'
ó
$ echo ó | grep '^..$'
(nothing)
使用C:
$ echo Ź | LC_ALL=C grep -i ź
(nothing)
$ echo ó | LC_ALL=C grep '[a-z]'
(nothing)
$ echo ó | LC_ALL=C grep '^..$'
ó
答案 1 :(得分:1)
在grep可以正确解释非ASCII文本之前,需要在 之前存在正确的语言环境文件,以便设置适当的环境变量。运行locale-gen en_US.UTF-8
,然后运行export LANG="en_US.UTF-8"
,您应该很好。如果这不起作用(或者如果您没有安装locale-gen
),请尝试export LANG=C.UTF-8
。
问题示例:
$ O_WITH_UMLAUT="ö"
$ printf "%s" "$O_WITH_UMLAUT" | grep -E '^[^\w]$'
$ printf "%s" "$O_WITH_UMLAUT" | grep -E '^[^\w]{2}$'
ö
第一次尝试没有产生输出,但是一旦你要求grep连续搜索两个非单词字符,那就是......
出现这种情况是因为非ASCII字符使用多字节编码方案(在这个时代应该几乎总是UTF-8,但古老/过时的系统可能使用更奇特的编码)。
$ printf "%s" "$O_WITH_UMLAUT" | od -Ax -tx1
000000 c3 b6
000002
注意:如果您的终端模拟器因为相关的编码问题而不允许您粘贴“ö”,那么您仍然可以将一个放入这样的环境变量中来测试: O_WITH_UMLAUT=$(printf "\xC3\xB6")
解决此问题的通常建议是将LANG
环境变量(作为LC_*
environment variables的后备)设置为en_US.UTF-8
(或en_GB.UTF-8
之类的内容,pl_PL.UTF-8
,ru_RU.UTF-8
,C.UTF-8
,你有什么......等等......)以便grep可以知道它应该为输入数据预期的编码:
$ export LANG="en_US.UTF-8"
...但是,如果这不起作用怎么办?
$ printf "%s" "$O_WITH_UMLAUT" | grep -E '^[^\w]$'
$ printf "%s" "$O_WITH_UMLAUT" | grep -E '^[^\w]{2}$'
ö
在这种情况下,首先要检查的是locale
的输出:
$ locale
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
看起来缺少某些区域设置文件。
locale-gen
联机帮助页的第一段解释了原因:
编译的区域设置文件占用大约50MB的磁盘空间,大多数用户只需要很少的区域设置。为了节省磁盘空间,编译的语言环境文件不在locales包中分发,但是通过运行locale-gen程序安装此包时会自动生成选定的语言环境。
所以,我们所要做的就是:
$ locale-gen en_US.UTF-8
Generating locales (this might take a while)...
en_US.UTF-8... done
$ locale # no more warnings!
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
$ printf "%s" "$O_WITH_UMLAUT" | grep -E '^[^\w]$' # works as it should!
ö
...但是,如果 不起作用怎么办?
$ locale-gen en_US.UTF-8
bash: locale-gen: command not found
绝望之下,您可以尝试C.UTF-8
,这几乎应该随处可用:
$ export LANG="C.UTF-8"
$ printf "%s" "$O_WITH_UMLAUT" | grep -E '^[^\w]$'
ö
如果仍不起作用,您可以尝试设置LC_ALL
(充当重手覆盖 )而不是LANG
(如前所述,它仅仅是后备)。
在您的情况下,您的非ASCII数据不是来自环境变量,而是文件系统上的目录(或者,更具体地,ls
选择该目录名称的文本表示...)所以最好注意一些文件系统(或它们的API,或ls
等工具...)将以不同于您预期的方式存储/生成信息,这可能导致类似(但不相关)问题。
例如,请考虑在Linux系统上执行以下操作:
$ mkdir -p /tmp/dirs
$ cd /tmp/dirs
$ python -i
>>> import os
>>> os.getcwd()
'/tmp/dirs'
>>> os.listdir('.')
[]
>>> # Create a directory with this name:
>>> # U+00F6: LATIN SMALL LETTER O WITH DIAERESIS
>>> # (total Unicode code-points: 1)
>>> os.makedirs('\xc3\xb6')
>>> os.listdir('.')
['\xc3\xb6']
>>> # Now create a directory with *this* name:
>>> # U+006F: LATIN SMALL LETTER O (ASCII)
>>> # followed by U+00A8: DIAERESIS (non-ASCII modifier)
>>> # (total Unicode code-points: 2)
>>> os.makedirs('o\xcc\x88')
>>> os.listdir('.')
['\xc3\xb6', 'o\xcc\x88']
>>> exit()
$ ls | grep -E '^[^\w]$'
ö
$ ls | grep -E '^[^\w]{2}$'
ö
$ ls -Fl
total 8
drwxr-xr-x 2 docker docker 4096 May 15 20:52 ö/
drwxr-xr-x 2 docker docker 4096 May 15 20:51 ö/
(混淆是怎么回事?!)
现在,同样的事情,在Mac OS X(HFS +)系统上,幸运的是 - 不允许这样的恶作剧,但是以你的文件/目录为代价,或许不在< em>完全你想象的方式:
>>> import os
>>> os.getcwd()
'/private/tmp/dirs'
>>> os.listdir('.')
[]
>>> os.makedirs('\xc3\xb6')
>>> os.listdir('.')
['o\xcc\x88'] # ...that's not what we asked it to create...
>>> os.makedirs('o\xcc\x88')
OSError: [Errno 17] File exists: 'o\xcc\x88'
>>> os.makedirs('\xc3\xb6')
OSError: [Errno 17] File exists: '\xc3\xb6'
>>> exit()
$ ls | grep -E '^[^\w]$' # nothing...
$ ls | grep -E '^[^\w]{2}$' # there it is.
ö
因此,一旦您确定您的语言环境已设置并正常运行,如果您的正则表达式仍不起作用,则接下来要检查的是确保您的文件系统(或您的文件系统)构建ls
,或者您在grep管道中使用的任何其他实用程序)都不会在幕后转码。 (我可以编织一条关于MinGW / MSYS实用工具和NTFS / exFAT的纱线,当我在那个特殊的封锁期间拉出我自己的头发时,头发的头发就会多了......但是,我离题了。)
希望有所帮助!
进一步阅读:
答案 2 :(得分:0)
非传统的“回答”,但我的回答是你的Ubuntu坏了,或者你需要和我一样使用相同的语言环境!我正在使用OSX Mavericks。
ls ??
<nothing>
ls ?
¨
ls ?| xxd
0000000: c2a8 0a ...
ls | grep "^\W\W$"
<nothing>
ls | grep "^\W$"
¨
echo $LANG
en_GB.UTF-8
答案 3 :(得分:0)
是的,你对@Ronald,grep
和Unicode有问题。
根据{{1}}:
man grep
但这个同义词不起作用。
The symbol \w is a synonym for [_[:alnum:]] and \W is a synonym for [^_[:alnum:]].