2025/02/12(水)Cygwin 3.5.7 における日本語ファイル名の扱い
- Cygwinと日本語ファイル名
- 標準系コマンド
- 自作プログラム 1 - ディレクトリエントリ読み込み (readdir())
- 自作プログラム 2 - ファイル名手動引き渡し (stat())
- perl
- linked library
- まとめ
Cygwinと日本語ファイル名
久しぶりに家のPCにCygwinを入れたら日本語ファイル名の扱いが変な感じなのでメモ。検証環境は以下の通り。
CYGWIN_NT-10.0-19045 hostname 3.5.7-1.x86_64 2025-01-29 19:46 UTC x86_64 Cygwin
端末は TeraTerm 5.4-dev (開発版snapshot)
環境変数LANGについて記載している場合、端末から入力した日本語ファイル名もそのLANGに沿った文字コードで入力している。
元々 Cygwin で日本語ファイル名を扱わないようにしていたので、元々この挙動なのか、そうでないのかはわからない。
標準系コマンド
どうもWindows と I/O する前に文字コードを変換しているようだ。環境変数に応じて、どのエンコードのファイル名でも使用できるように思われる。
LANG=ja_JP.UTF-8の時
出力は UTF-8 で正常
ls echo * ls * ls -d * | xargs file -- find find | cat find | xargs file --
LANG=ja_JP.SJIS の時
出力は SJIS で正常
ls echo * ls * ls -d * | xargs file -- find find | cat find | xargs file --
一部出力に問題あり
ls -lファイル名以外にUTF-8が混じる。
LANG=C の時
出力は エスケープ表現で正常
ls ls * ls -d * | xargs file --
出力は UTF-8
echo *
一部出力に問題あり
ls -lファイル名はエスケープ表現だが、ファイル名以外にUTF-8が混じる。
自作プログラム 1 - ディレクトリエントリ読み込み (readdir())
以下のような単純なプログラムを gcc でコンパイルして使用した。ソースコード readd.c
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
int main()
{
DIR *dirp;
struct dirent *dp;
struct stat st;
dirp = opendir(".");
if (dirp == NULL) {
perror("opendir");
return 1;
}
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_type & DT_DIR) {
printf("<%s> DIR \t", dp->d_name);
}
else {
printf("<%s> \t", dp->d_name);
}
if (stat(dp->d_name, &st) == 0) {
printf("%lld, %s", st.st_size, ctime(&st.st_mtime));
}
else {
perror("");
}
}
(void)closedir(dirp);
return 0;
}
動作結果
locale に関わらず、ファイル名は必ず UTF-8 で出力された。- 確認済みlocale
- LANG=ja_JP.SJIS
- LANG=ja_JP.UTF-8
- LANG=C
自作プログラム 2 - ファイル名手動引き渡し (stat())
以下のような単純なプログラムを gcc でコンパイルして使用した。ソースコード statf.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
int main(int argc, char *argv[])
{
struct stat st;
int i = 1;
while (i < argc) {
if (stat(argv[i], &st) == 0) {
printf("<%s> %lld, %s", argv[i], st.st_size, ctime(&st.st_mtime));
}
else {
perror(argv[i]);
}
i++;
}
return 0;
}
動作結果
LANG=ja_JP.SJIS
SJIS のファイル名を引き渡したと思われる全てのケースで失敗する。- コマンドラインも SJISである。
- No such file or directory になる。
./statf テスト01.txt find | xargs ./statf ls | xargs ./statf ./statf *おそらくファイル名として UTF-8 を指定する必要がある。
$ ./statf *01.txt テスト01.txt: No such file or directory以下のように UTF-8 のファイル名を用意して実行すると成功する。
user@hostname ~/src $ cat filename.txt 繝せ繝01.xt user@hostname ~/src $ ./statf $(cat filename.txt) <繝せ繝01.xt> 26, Wed Feb 12 19:00:59 2025 user@hostname ~/src $
LANG=ja_JP.UTF-8
全てのケースで正常に実行される。./statf テスト01.txt find | xargs ./statf ls | xargs ./statf ./statf *
perl
以下の perl コードは SJIS, UTF-8 どちらでも動作した。
perl -e 'while($n=<*.txt>){ print "file=<$n>"; open(F,"<",$n)||die; @b=(<F>);print scalar(@b) ."\n"; close(F) }'
perl -e 'opendir(D,".");while($n=readdir(D)){ print "file=<$n>"; open(F,"<",$n)||die; @b=(<F>);print scalar(@b) ."\n"; close(F) }'
linked library
user@hostname ~/src
$ ldd statf.exe
ntdll.dll => /cygdrive/c/WINDOWS/SYSTEM32/ntdll.dll (0x7ffa79c90000)
KERNEL32.DLL => /cygdrive/c/WINDOWS/System32/KERNEL32.DLL (0x7ffa79200000)
KERNELBASE.dll => /cygdrive/c/WINDOWS/System32/KERNELBASE.dll (0x7ffa777a0000)
cygwin1.dll => /usr/bin/cygwin1.dll (0x7ffa11520000)
user@hostname ~/src
$ ldd /bin/ls
ntdll.dll => /cygdrive/c/WINDOWS/SYSTEM32/ntdll.dll (0x7ffa79c90000)
KERNEL32.DLL => /cygdrive/c/WINDOWS/System32/KERNEL32.DLL (0x7ffa79200000)
KERNELBASE.dll => /cygdrive/c/WINDOWS/System32/KERNELBASE.dll (0x7ffa777a0000)
cygintl-8.dll => /usr/bin/cygintl-8.dll (0x3f7ef0000)
cygwin1.dll => /usr/bin/cygwin1.dll (0x7ffa11520000)
cygiconv-2.dll => /usr/bin/cygiconv-2.dll (0x3f7fe0000)
まとめ
- 基本的に Cygwin (v3.5.7) のレイヤーでは UTF-8 でファイル名を受け渡しするのが最も安心できる。
- ls, find のような基本的なGNUコマンド類が多言語対応しているが、これの UTF-8 以外で出力したファイル名をそのまま UTF-8 ロケールの自作プログラムに渡すと正常動作しない
ファイル名も SJIS で扱えると助かるのだが、自作プログラムではそのままではうまくいかない事がわかった。