打ち込めないような名前のファイルでもシェルで操作する方法

今日は小ネタです。
日本語のファイル名だったり、操作ミスなどで制御文字を含んだような名前のファイルができてしまった際に、そのファイルをシェル上で消したりリネームしたりする方法をご紹介。

1. "ls -li" で該当ファイルの inode 番号を取得

まず、対象のinode番号を取得します。たとえばこんな感じです。

$ ls -li
合計 0
2490401 -rw-r--r-- 1 over80 over80 0  4月 17 23:04 binname_???????????????_.txt
2490385 -rw-r--r-- 1 over80 over80 0  4月 17 23:02 日本語ファイル名.txt

一番最初のカラムに表示されている数字が、そのファイルの inode 番号です。この番号はそのファイルシステム下で一意なので、ファイル名の代わりにこの番号でファイルを指定するわけです。

ちなみに、上記の例の1つめのファイルは、16進で "0x01" から "0x0f" の制御文字を含んだ名前のファイルにしてみました。"ls" はその端末の locale で表示できない文字は "?" で置き換えてくれるので、表示は崩れません。この機能はオプションで明示するか、パイプ等でターミナル以外に出力する場合は off になります。

$ ls | hexdump -C
00000000  62 69 6e 6e 61 6d 65 5f  01 02 03 04 05 06 07 08  |binname_........|
00000010  09 0a 0b 0c 0d 0e 0f 5f  2e 74 78 74 0a e6 97 a5  |......._.txt....|
00000020  e6 9c ac e8 aa 9e e3 83  95 e3 82 a1 e3 82 a4 e3  |................|
00000030  83 ab e5 90 8d 2e 74 78  74 0a                    |......txt.|
0000003a
2. "find -inum (inode番号)" で得たファイル名をコマンドに渡す

たとえば、"日本語ファイル名.txt" (i-node 番号は 2490385) を mv で ascii.txt にリネームする場合、こうします。

$ mv "$(find -inum 2490385)" ascii.txt

シェルで $(コマンド) と書くと、そのコマンドを実行した結果がそこに埋め込まれます(バッククオートで括るのと同じですが、ここでは見やすさ重視でこちらで書いています)。ファイル名に空白文字などが入っていると、そのままではそれが再評価されてしまうので、ダブルクォートで括って再評価を避けています。

なお、わざわざ一旦ファイル名を出力しなくても、find の -exec オプションで同等のことができますので、そちらでやっても良いでしょう。

$ find -inum 2490385 -exec mv "{}" ascii.txt ";"

ただ、find に慣れている人であればこちらが素直だと思いますが、慣れていない人に教えるには find の引数の与え方が特殊なので、前者の方法でやっていただいた方が良いかと思います。


(以下、2014-06-08 追記)
なお、 find は周知の通り、指定したディレクトリ(デフォルトはカレントディレクトリ)から、再帰的にファイルを探しにいきますので、サブディレクトリ等がたくさんある場合は時間がかかります。今回のようにカレントディレクトリに存在すると分かっている場合は、探索の深さを制限してやるとよいでしょう。

$ mv "$(find -inum 2490385 -maxdepth 1)" ascii.txt
$ find -inum 2490385 -maxdepth 1 -exec mv "{}" ascii.txt ";"