問題が起こりやすいケース
問題がよく起こるのはファイル名を付けるとき、
最初のファイルには数字など付けずに、
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