这让我发疯了.有下一个bash脚本.
testdir="./test.$$"
echo "Creating a testing directory: $testdir"
mkdir "$testdir"
cd "$testdir" || exit 1
echo "Creating a file word.txt with content á.txt"
echo 'á.txt' > word.txt
fname=$(cat word.txt)
echo "The word.txt contains:$fname"
echo "creating a file $fname with a touch"
touch $fname
ls -l
echo "command: bash cycle"
while read -r line
do
[[ -e "$line" ]] && echo "$line is a file"
done < word.txt
echo "command: find . -name $fname -print"
find . -name $fname -print
echo "command: find . -type f -print | grep $fname"
find . -type f -print | grep "$fname"
echo "command: find . -type f -print | fgrep -f word.txt"
find . -type f -print | fgrep -f word.txt
Run Code Online (Sandbox Code Playgroud)
在Freebsd上(也可能在Linux上)给出了结果:
Creating a testing directory: ./test.64511
Creating a file word.txt with content á.txt
The word.txt contains:á.txt
creating a file á.txt with a touch
total 1
-rw-r--r-- 1 clt clt 7 3 júl 12:51 word.txt
-rw-r--r-- 1 clt clt 0 3 júl 12:51 á.txt
command: bash cycle
á.txt is a file
command: find . -name á.txt -print
./á.txt
command: find . -type f -print | grep á.txt
./á.txt
command: find . -type f -print | fgrep -f word.txt
./á.txt
Run Code Online (Sandbox Code Playgroud)
即使在Windows 7(安装了cygwin)中运行脚本也能得到正确的结果.
但是当我在OS X bash 上运行这个脚本时,得到了这个:
Creating a testing directory: ./test.32534
Creating a file word.txt with content á.txt
The word.txt contains:á.txt
creating a file á.txt with a touch
total 8
-rw-r--r-- 1 clt staff 0 3 júl 13:01 á.txt
-rw-r--r-- 1 clt staff 7 3 júl 13:01 word.txt
command: bash cycle
á.txt is a file
command: find . -name á.txt -print
command: find . -type f -print | grep á.txt
command: find . -type f -print | fgrep -f word.txt
Run Code Online (Sandbox Code Playgroud)
所以,只有bash
找到文件á.txt
没有,find
也没有grep
.:(
首先询问apple.stackexchange和一个建议使用iconv
转换文件名的答案.
$ find . -name $(iconv -f utf-8 -t utf-8-mac <<< á.txt)
Run Code Online (Sandbox Code Playgroud)
虽然这适用于"OS X",但无论如何它都很糟糕.(需要为每个输入到终端的utf8字符串输入另一个命令.)
我正试图找到一个通用的跨平台bash编程解决方案.所以,问题是:
bash
"找到"文件而find
不是?和
iconv
?perl
?Ps:最后,不是真正的编程问题,但想知道Apple使用分解的文件名决定什么是理由不能用命令行很好地解决 utf8
编辑
简单od
.
$ ls | od -bc
0000000 141 314 201 056 164 170 164 012 167 157 162 144 056 164 170 164
a ? ** . t x t \n w o r d . t x t
0000020 012
\n
Run Code Online (Sandbox Code Playgroud)
和
$ od -bc word.txt
0000000 303 241 056 164 170 164 012
á ** . t x t \n
0000007
Run Code Online (Sandbox Code Playgroud)
所以
$ while read -r line; do echo "$line" | od -bc; done < word.txt
0000000 303 241 056 164 170 164 012
á ** . t x t \n
0000007
Run Code Online (Sandbox Code Playgroud)
并且从一个发现的出口是相同的 ls
$ find . -print | od -bc
0000000 056 012 056 057 167 157 162 144 056 164 170 164 012 056 057 141
. \n . / w o r d . t x t \n . / a
0000020 314 201 056 164 170 164 012
? ** . t x t \n
Run Code Online (Sandbox Code Playgroud)
因此,word.txt
IS 的内容不同于从其内容创建的文件.因此,仍然没有解释为什么bash
找到该文件.
Unicode 很难。每次刷牙时都要重复一遍。
您的á.txt
文件名包含 5 个字符,其中á
是麻烦的一个。有不止一种方法可以表示á
为 Unicode 代码点序列。有预先组合的表示和分解的表示。不幸的是,大多数软件都没有准备好处理字符,而是解决了代码点(是的,大多数软件都是 cr*p)。这意味着给定相同字符的预组合和分解表示,软件不会将它们识别为相同。
您有一个预组合的á
,表示为 Unicode 代码点 U+00E1 带有 ACUTE 的拉丁文小写字母 A。Windows 使用预组合表示。Mac 文件系统坚持分解表示(嗯,主要是;utf-8-mac 不分解某些字符范围,但á
分解正常)。因此,在 Mac 上,您á
会变成 U+0061 拉丁小写字母 A,然后是 U+0301 组合 ACUTE ACCENT(记下我的头顶,手头没有 Mac)。Linux 文件系统接受你扔给它们的任何东西。
如果你给find
一个 precomposed á
,它不会找到一个á
名字中带有分解的文件,因为它不准备处理这个骚动。
那么有什么解决办法呢?没有。如果要处理 Unicode,则必须解决常用工具的缺陷。
这是一种稍微不那么丑陋的解决方法。编写一个小bash
函数(使用iconv
或其他),为每个系统转换该系统可接受的表示,并在整个过程中使用它。让我们称之为u8
:
find . -name $(u8 $myfilename) -print
find . -name -type f -print | fgrep $(u8 $myfilename)
Run Code Online (Sandbox Code Playgroud)
等等。漂亮不是,但它应该工作。
哦,我认为我们都应该开始为此 cr*p 发送错误报告。我们的软件最终应该努力理解基本的人类概念,比如字符(我什至还没有开始谈论字符串)。代码点只是不会削减它,抱歉,即使它们是 Unicode 代码点。