問題が起こりやすいケース
問題がよく起こるのはファイル名を付けるとき、
最初のファイルには数字など付けずに、
2つ目以降には_1.txt等、アンダーバーを付けて数字を付けるケース。
aaa.txt
aaa_1.txt
aaa_2.txt
...
みたいな。
自分で気をつけていてもソフトによってこの様なファアイル名の付け方を 勝手にするものもあるのでファイルとして持ってることはあると思います。
ロケールによる違い
lsやsortコマンド等で一覧をソートする場合、
環境設定によって順序が変わってくる事があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
こんな感じのファイルを用意してsortしてみます。
行ってる環境はLinuxです。
ロケールをCにしてやってみます。
$ LC_ALL=C sort test.txt
A,1
A-1
A.1
A1
AA
A_1
a,1
a-1
a.1
a1
a_1
aa
まず記号の,、-、.が来て、次に数字、文字と来て
最後に_が来ます。
また、大文字が先で小文字が後です。
これをUTFなロケールにしてみると
$ LC_ALL=en_US.UTF-8 sort test.txt
a1
a_1
a-1
a,1
a.1
A1
A_1
A-1
A,1
A.1
aa
AA
こんな感じになります。
まず、小文字が最初に来ています。
ただ、第一文字で全てソートされず、aaと文字列が続くものは
最初が小文字でも第二文字が記号や数字のものより下に来ています。
数字、記号でのソートは
数字、_、-、,、.の順。
Cでは最後に来ていた_が数字の次に来ています。
.と_の順序が前後しているので
最初に書いた問題が起こりやすいケースの
_1.txtの様に数字をファイル名に付ける要な場合、
lsなりsortなりで順序を整えようとすると
- LC_ALL=C: aaa.txt, aaa_1.txt, aaa_2.txt
とCではaaa.txtが最初に来るのに対し
- LC_ALL=en_US.UTF-8: aaa_1.txt, aaa_2.txt, aaa.txt
とen_US.UTF-8では最後に来てしまいます。
もし、読み込む順序が意味あるものであれば 後者の方では間違った結果を得る可能性があります。
sortのマニュアルを見てみると
** WARNING ** The locale specified by the environment affects sort order. Set LC_ALL=C to get the traditional sort order that uses native byte values.
とあります。
この様な記号が入ってくる可能性のあるsortや
順番を気にする時に使うlsなんかでは
LC_ALL=C sort test.txt
の様にロケールを必ずCにして実行する様にすべきだ、ということです。
ロケール設定
上ではLC_ALLを設定していますが、
これ以外にもロケールを設定する値があります。
sort等で使われるのは
LC_CORRATEとLANGです。
まず、LC_ALLを見て、設定されてない場合、LC_CORRATE、LANGと見ていきます。
LC_ALLに関しては、これを設定すると
全てのLC_XXXの値を上書きします。
LC_ALLを設定した後にLC_CORRATE等を設定しても
LC_ALLに上書きされます。
ロケール設定を見るにはlocaleというコマンドを使います。
$ env | grep LC_
$ env | grep LANG
$
とLC_XXX、LANGが何も設定されてない状態で見てみると
$ locale
LANG=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
$
このPOSIXロケールは大概の場合Cと同じもので、
Unixの基本的な設定になります。
ここでLANGを設定してみると
$ export LANG=en_US.UTF-8
$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
...
LC_ALL=
$
と言った感じでLC_ALL以外のLC_の値がLANGと同じになります。
各LC_の値を設定してみると
$ export LC_CTYPE=C
$ locale
LANG=en_US.UTF-8
LC_CTYPE=C
LC_NUMERIC="en_US.UTF-8"
...
LC_ALL=
$
と、設定したものだけ変更されます。
また、直接設定されたものは"が付きません。
設定時に付けても
$ export LC_CTYPE="C"
$ locale
LANG=en_US.UTF-8
LC_CTYPE=C
LC_NUMERIC="en_US.UTF-8"
...
LC_ALL=
$
同じ。
ここで、LC_ALLを設定すると
$ export LC_ALL=ja_JP
$ locale
LANG=en_US.UTF-8
LC_CTYPE="ja_JP"
LC_NUMERIC="ja_JP"
...
LC_ALL=ja_JP
$
と、LC_XXXは全てLC_ALLと同じになります。
それぞれを設定しなおしても
$ export LC_CTYPE=C
$ locale
LANG=en_US.UTF-8
LC_CTYPE="ja_JP"
LC_NUMERIC="ja_JP"
...
LC_ALL=ja_JP
$
と変更されません。
各LC_XXXで設定を変えたい場合にはunset LC_ALLとして
LC_ALLを削除する必要があります。
Mac (BSD)では
Macでも同じ様にロケールを変更してソートをしてみましたが
何に変更しても全て上のCにした結果と同じになりました。
Macに入ってるsortコマンドがBSD用だったりして違うのかな、と思って見てみたところ、
$ /usr/bin/sort
...
Report bugs to <[email protected]>.
と、どうもGNUのsortと同じ物を使ってる様子。
manとか見る限りでは全く一緒です。
terminal - How to replace Mac OS X utilities with GNU core utilities? - Ask Different
この辺を見る限り、少なくとも以前まではBSD用のsort
1
を使ってた模様。
ただ、
sort(1) : FreeBSD 一般コマンドマニュアル: http://freebsd4-jman.kandk.co.jp/1/sort.1.html
とかでもGNUのsortを説明していたりするのでBSD系全体として
sortはGNUの物へ、となって来たのでしょうか。。。?
で、話がずれましたが、今回の話では逆に
同じsortを使っているがLinuxとMacで挙動が違う、と言う話です。
なのでちょっと追いきれてませんが、ロケール設定ファイルの方の 違いがあるのだと思います。
参考:
sort - Why does ls sorting ignore non-alphanumeric characters? - Unix & Linux Stack Exchange
