2013-07-03 10 views
9

それは私を狂ってしまう。次のbashスクリプトを用意してください。でも、(Cygwinがインストールされている)は、Windows 7は、スクリプトを実行すると正しい結果を与えるでユニコードのファイル名を使用した移植可能な(クロスプラットフォームの)スクリプト

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 

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上)

は結果を与えます。

しかし、私は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なし、findgrepを見つけました。 :(ファイル名を変換するためのiconvを使用するapple.stackexchangeとone answer suggestingに最初に尋ねた。

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

これは、「OSのX」のための作品ですが、それはとにかくひどいですが

が。(別のものを入力して必要。端末にどのような着信するすべてのutf8文字列のためのコマンド)

私は、一般的なクロスプラットフォームのbashのプログラミング解決策を見つけるためにしようとしているので、質問は次のとおりです。

  • OS Xの場合、bashはファイルが見つかりましたが、findは見つかりませんでしたか?

  • Unicodeのファイル名がファイルに保存されているクロスプラットフォームのbashスクリプトの書き方。
  • のみソリューションはiconv特別バージョンのみOS Xのために書くのですか?
  • は、perlのような他のスクリプト言語のための移植可能なソリューションですか?

シモンズ:そして最後に、実際に質問をプログラミングするが、コマンドラインutf8

EDIT

とうまく再生されないものを分解したファイル名を使用して、Appleの決断の根拠は何か不思議に思っていませんシンプル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 

とoutpoutので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の内容が異なっているものをファイルそのコンテンツから作成されます。したがって、まだbashがファイルを見つけた理由を説明できません。

答えて

3

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

á.txtのファイル名に5文字含まれています。そのうちáは面倒なものです。 Unicodeコードポイントのシーケンスとしてáを表す複数の方法があります。あらかじめ作成された表現と分解された表現があります。残念なことに、ほとんどのソフトウェアは文字を扱う準備ができておらず、代わりにコードポイントに定住しています(ほとんどのソフトウェアはcr * pです)。これは、同じ文字の事前合成され分解された表現が与えられた場合、ソフトウェアはそれらを同じものとして認識しないことを意味する。

áは、UnicodeコードポイントU + 00E1 LATIN SMALL LETTER A WITH ACUTEとして表されています。 Windowsは事前合成された表現を使用します。 Macファイルシステムは、分解された表現を主張しています(ほとんどの場合、utf-8-macは特定の文字範囲を分解しませんが、áは分解されます)。したがって、MacではáはU + 0061ラテンスモールレターAとなり、その後にU + 0301コンバインドACUTEアクセント(私の頭の上を筆記し、Macを手に持たない)になります。 Linuxのファイルシステムは、あなたが投げたものを受け入れます。それがこの大騒ぎに対処する準備ができていないだから

あなたは合成済みáfindを与えた場合、それは、その名前に分解áを持つファイルを見つけることができません。

解決策は何ですか?何もありません。Unicodeを処理する場合は、共通ツールの不具合を回避する必要があります。

ここでは少し醜い回避策があります。 bash関数(iconvなどを使用)を使用して、システムごとにそのシステムで受け入れ可能な表現を変換し、それを一貫して使用するようにします。それをu8

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

などと呼びましょう。それほどではありませんが、うまくいくはずです。

私は皆、このcr * pのバグレポートを送るべきだと思います。私たちのソフトウェアは、最終的に文字のような基本的な人間の概念を理解しようと努めなければなりません(私はストリングについて話を始めていません)。コードポイントはそれをカットしません。ユニコードコードポイントであっても、残念です。

+0

したがって、 'bash'( OS X)は、ファイル名を分解した 'á'やfind、grepなどの他のユーティリティを探しているときに、あらかじめ作成された'á'を内部的に正しく変換します。だから、 "移植可能な"スクリプトを書いているのは、可能であれば "純粋なbash"だけを使っているのです...そうですか? – jm666

+0

*したがって、bash(OS X用にコンパイルされたもの)は、内部的にprecomposedá*を変換するのはなぜですか?私はソースを見ていないが、それは正しいことだ。 –

+0

OK、答えを受け入れる - 主に: "ユニコードは難しい"と "ソフトウェアは駄目です" :) :) - 私の追加:ほとんどのOSは、ソフトウェアは、19世紀のレベルであり、ユーザーのニーズについては何も知らない。 Shigh。ありがとう:) – jm666

2

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

find -namefind -printを使用してその存在をテストすると、異なるエンコードが使用されているようです。 find -printの出力を16進数のパイプ(xxdまたはod -xまたは類似のもの)にパイプすることを提案します。これはおそらく、-printを使用しているときにfindが使用するエンコーディングを表示します(これはおそらく-nameを使用する場合にも使用されます)。

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

+0

残念ながら、編集を参照してください。 'bash'がファイルを見つけた理由はまだ説明していません。そして、まだ解決策はありませんスクリプトを書く方法は何か(主要な)プラットフォーム、つまりLinux、FreeBSD、OS X、そしておそらくwindows/cygwinでも正しく動作するでしょう... – jm666

関連する問題