11

それは私を夢中にさせています。次の 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

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

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

そのためbash、ファイルが見つかりá.txtませfindんでしgrepた。:(

最初にapple.stackexchangeで質問され、ファイル名の変換に使用することを提案する1つの回答。iconv

$ find . -name $(iconv -f utf-8 -t utf-8-mac <<< á.txt)

これは「OS X」では機能しますが、とにかくひどいです。(端末に入力するutf8文字列ごとに別のコマンドを入力する必要があります。)

一般的なクロスプラットフォームの bash プログラミング ソリューションを見つけようとしています。したがって、質問は次のとおりです。

  • OS X でbashファイルが「見つかった」のに見つからないのfindはなぜですか?

  • Unicode ファイル名がファイルに保存されるクロスプラットフォームの bash スクリプトを作成する方法。
  • 唯一の解決策は、? を使用して OS X 専用の特別なバージョンを作成することです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   

$ od -bc word.txt
0000000   303 241 056 164 170 164 012                                    
           á  **   .   t   x   t  \n                                    
0000007

だから

$ 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

検索からの出力は次と同じです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      

そのため、word.txtそのコンテンツから作成されるファイルは IS DIFFERENT とは異なります。bashしたがって、ファイルが見つかった理由はまだ説明されていません。

4

2 に答える 2

5

ユニコードは難しい。歯を磨くたびにそれを繰り返します。

ファイルá.txt名には 5 文字が含まれてáおり、そのうちの 1 つが厄介です。á一連の Unicode コード ポイントとして表す方法は複数あります。事前に構成された表現と、分解された表現があります。残念ながら、ほとんどのソフトウェアは文字を処理する準備ができておらず、代わりにコードポイントで解決しています (はい、ほとんどのソフトウェアは cr*p です)。これは、同じ文字の合成済み表現と分解済み表現が与えられた場合、ソフトウェアはそれらを同じものとして認識しないことを意味します。

áUnicode コード ポイント U+00E1 LATIN SMALL LETTER A WITH ACUTE として表される事前構成された があります。Windows は、構成済みの表現を使用します。Mac ファイルシステムは、分解された表現を主張します (ほとんどの場合、utf-8-mac は特定の文字範囲を分解しませんが、問題なく分解されますá)。したがって、Mac では、áU+0061 LATIN SMALL LETTER A の後に U+0301 COMBINING ACUTE ACCENT が続きます (Mac が手元にないので、頭のてっぺんから書き留めます)。Linux ファイルシステムは、投げたものを何でも受け入れます。

findprecomposedを指定すると、名前にádecomposed が含まれるファイルが見つかりません。これは、この大騒ぎを処理する準備ができていないためです。á

それで、解決策は何ですか?ありません。Unicode を処理したい場合は、共通ツールの欠陥を回避する必要があります。

これは、やや醜い回避策の 1 つです。システムごとにそのシステムで受け入れられる表現に変換する小さなbash関数を (使用するか何かを使用して) 記述し、それを全体で使用します。iconvそれを呼びましょうu8

find . -name $(u8 $myfilename) -print 
find . -name -type f -print | fgrep $(u8 $myfilename)

等々。かなりそうではありませんが、うまくいくはずです。

ああ、私たち全員がこの cr*p のバグ レポートを送信し始めるべきだと思います。私たちのソフトウェアは、最終的には文字などの基本的な人間の概念を理解するよう努める必要があります (文字列についてはまだ話し始めていません)。Unicode コード ポイントであっても、コード ポイントは役に立ちません。

于 2013-07-03T13:07:17.067 に答える
2

を使用してファイルを作成し、touchその存在をテストすると[[ -e "$line" ]]、同じエンコーディングが使用されるため、ファイルが見つかります。

find -nameとを使用してその存在をテストするとfind -print、異なるエンコーディングが使用されているようです。の出力をfind -printhexdumper (xxdまたはod -x同様のもの) にパイプすることを提案します。findこれはおそらく、 を使用するときにどのエンコーディングが使用されるかを示します-print(これは、 を使用するときにも使用される可能性があります-name)。

エンコーディングの問題に対する一般的な解決策は常に次のとおりです。あなたの場合、どのポイントが採用しやすいかを決定する必要があります。ファイルの作成時にエンコーディングを変更するtouch "$(iconv -f utf-8 -t utf-8-mac <<< á.txt)"か( )など)、与えるものを変更できますfind(質問ですでに与えられている解決策)。それbash自体はユニコードのファイル名にうまく対処しているfindようで、この問題しかないように見えるので、そこで必要な変換を行うことも提案します。-nameおそらく、Mac OS find バージョンの構成オプションで、コマンドに使用するエンコーディングを指定することさえあり-printます。

于 2013-07-03T11:49:45.830 に答える