日本語文字列が含まれるファイル名で文字化けしない rename を使おう

■rename ワイドキャラクター対応 renamej TEST

追記訂正2016-04-14:うっかりしていました。
このままだとワイドキャラクターで盛大に化けたり、無効なエンコードになります。テスト環境では、
Rename.pmにもパッチを当てていました。そのうち公開します。

以下、一旦 、一般向けのお知らせとしては取り下げます。

作成2015-06-02 1年前作成メモ、ubuntu14.04/mint17はこれでOK。16.04LTSでは少し変わっています。自分向けメモで、かつ下書きですが公開します。libutf8-all-perlの使い方がキモです。12.04時代の古いrename.pmであれば違います。必要ならコメントください。当時作成した対応モジュールをお知らせします。

■エキスパート向け3行(理解できない場合実行しないで)


sudo apt install libutf8-all-perl libfile-rename-perl
cd ~/bin && cp `which rename` .
perl -pe "s/(^use\sPod::Usage;)(\n)/\1\nuse utf8::all;/" rename > renamej


もしかすると、16.04β2では
sudo apt install rename libutf8-all-perl libimport-into-perl libmodule-runtime-perl libparams-classify-perl libscalar-number-perl

いずれにしても依存関係で導入が求められるファイルはそのまま入れて構わないです。


もうひとつ作業があります。

find . -name Rename.pm ま、下にあるってわかっていますけど。

/usr/share/perl5/File/Rename.pm をバックアップ。これを書き換えます。
バックアップとらなくても、apt install --reinstall で戻せると思います。

これにも、utf8::all を追加。頭に。

これでだいたい「オリジナルのままだと、yで日本語が化ける処理(あまりに壮大な化け方なのでびっくりする)」がうまくいくようになると思います。

renamej -n "y/0-9/0-9/" *.* とか、その逆とか。
A-Z A-Z 。試験ファイルを作って試してみてください。(わかりにくいですが、全角と半角の違いです)


不具合があるかもしれません。間違ってもオリジナルの作者の方には問い合わせないでください。使っている主要モジュールは、アメリカ人とイギリス人の方です。一つ前のバージョンのほうがユーザーのフロントエンドがいじりやすいのでよかったんですが、そのあたりの混乱が私にもありました。

■使い方


renamej を代替コマンドとして使うのみ。
renamej -h

自分はRename.pmの出力とか修正したものを使っています。日本語化含めて。一度転換ミスがあってファイルが消えたので公開できません。どっかにバグがあるので。ちょっと違って、修正していとだめでした。ごめんなさい。

■余談:なぜワイドキャラクター対応にしなければならないのか。


文字化けしたファイル名を作りたくないのはもちろん。自在に扱いたいし、せっかくのperl正規表現で、yとか使えないって悲しすぎる。盛大な文字化け(というよりも、意図しない文字の背番号に置き換わるのが症状)。なぜか指摘する人がいない?! そういうオプションは使ってないのか?!

これを解消し、最小の手間でワイドキャラクター/マルチバイトのファイル名を扱うことが目的。
目的は達成できました。

■余談2:モジュールを導入するとrename は置換される。(14.04は古いバージョンです。これを使いたい場合は、Rename.pmに修正を施す必要があります)


sudo apt-get install libutf8-all-perl libfile-rename-perl



libfile-rename-perl (0.20-1) を設定しています ...
update-alternatives: /usr/bin/rename (rename) を提供するために 自動モード で /usr/bin/file-rename を使います

※rename が置き換わります。前のバージョンがよければコピーして残しておきます。結果的にいえば、libfile-rename-perl モジュールを使うrenameに置換されます。微妙に機能拡張されています。cpan に2つのモジュールのページがありますので、ぜひ参照してください。

但しいずれもUTF-8/ワイドキャラクターを空気のように取り扱えない*1ため、同時にインストールした libutf8-all-perl を use するコマンドに書き換えます。

■本論:rename ワイドキャラクター対応版


前提:ubuntu/mint そして、gnome(ubuntu)

方法:Perlモジュールのインストールからツール作成まで

~/bin にパスを通していることが前提です。

#メンテナーCPANモジュール導入*2
sudo apt-get install libutf8-all-perl libfile-rename-perl
#コピーしてくる。
cd ~/bin && cp `which rename` .
#対応品に書き換え/作成
perl -pe "s/(^use\sPod::Usage;)(\n)/\1\nuse utf8::all;/" rename >renamej
#リネームしとく。為念。
mv rename rename.org

これで、renamej ができあがります。既にパスが通ったディレクトリに配置されていますので、renamej として下さい。たいしたテストはしていませんが自分の範囲ではパーフェクトではないか、と。

以下、打鍵

renamej -h
renamej -m

これがもっとも簡便なワイドキャラクター対応でした。エンコードしたりデコードしたりUTF-8フラグを外したり、暇人としか思えないコード付加の不毛な時間は自分の範囲では不要になりました。

なお、既存インストール状態の rename にも使えます。その場合、utf8:all モジュールだけでOK。renameモジュール利用の場合は、表示を制御するためには、モジュールを書き換える必要があります。既存の場合は、renameコマンドを直に書き換えればいいので使い勝手が自分には良いです。

ローカルで使うのでツール名はなんでもいいんですが、ren にするのはどうかな、と。ちなみにMS-DOSみたいなリネームは、mren というのが用意されています(気のせいかも、前はあったような気がした)。

盛大な化け方がなくなると思います。
(うっかり数値を全角で入力してしまっているファイル名を正規化)

renamej -n "y/0-9/0-9/" *.*

*1:空気のように。いわゆるアスキー世界の256文字程度だけでこのコマンドを使っている限り問題は起きないです。UTF-8の日本語などが含まれている部分がちゃんと扱えないのは、僕たち日本人に限らず残念なことです。オリジナルをラリーさんが作ったのは何せ昔の話。中途半端に日本語が含まれる文字列が処理できてしまうのが罪作りな easy rename 君です。

*2:僕はCPANから導入しました。このドキュメントを書くために調べていたら、synapatic でメンテナー版が簡易導入できることが判明したので、それに合せています。誰にでも簡単ですし。

■たぶん


MAC OS X でもperlにこのふたつのモジュールいれて、renamejをどこかに配置すれば、同じように扱えると思います。MACで動いたらぜひ教えてください。

■ソースコード


以下の通り、ベタ貼り

renamej
------------
#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell
# $Revision: 331 $$Date: 2013-04-30 21:23:41 +0100 (Tue, 30 Apr 2013) $
# Robin's RCS header:
# RCSfile: rename.PL,v Revision: 1.3   Date: 2006/05/25 09:20:32
# Larry's RCS header:
#  RCSfile: rename,v   Revision: 4.1   Date: 92/08/07 17:20:30
#
#  Log: rename,v
# Revision 1.5  1998/12/18 16:16:31  rmb1
# moved to perl/source
# changed man documentation to POD
#
# Revision 1.4  1997/02/27  17:19:26  rmb1
# corrected usage string
#
# Revision 1.3  1997/02/27  16:39:07  rmb1
# added -v
#
# Revision 1.2  1997/02/27  16:15:40  rmb1
# *** empty log message ***
#
# Revision 1.1  1997/02/27  15:48:51  rmb1
# Initial revision
#

use strict;
use File::Rename ();
use Pod::Usage;
use utf8::all;
main() unless caller;

sub main {
    my $options = File::Rename::Options::GetOptions
        or pod2usage;

    mod_version() if $options->{show_version};
    pod2usage( -verbose => 2 ) if $options->{show_manual};
    pod2usage( -exitval => 1 ) if $options->{show_help};

    @ARGV = map {glob} @ARGV if $^O =~ m{Win}msx;

    File::Rename::rename(\@ARGV, $options);
}

sub mod_version {
    print __FILE__ .
    ' using File::Rename version '.
        $File::Rename::VERSION ."\n\n";
    exit 0
}  

1;

__END__

=head1 NAME

rename - renames multiple files

=head1 SYNOPSIS

B
S<[ B<-h>|B<-m>|B<-v> ]>
S<[ B<-v> ]>
S<[ B<-n> ]>
S<[ B<-f> ]>
S<[ B<-e>|B<-e> I]*|I>
S<[ I ]>

=head1 DESCRIPTION

C
renames the filenames supplied according to the rule specified as the
first argument.
The I
argument is a Perl expression which is expected to modify the C<$_>
string in Perl for at least some of the filenames specified.
If a given filename is not modified by the expression, it will not be
renamed.
If no filenames are given on the command line, filenames will be read
via standard input.

For example, to rename all files matching C<*.bak> to strip the extension,
you might say

    rename 's/\e.bak$//' *.bak

To translate uppercase names to lower, you'd use

    rename 'y/A-Z/a-z/' *

=head1 OPTIONS

=over 8

=item B<-v>, B<-verbose>

Verbose: print names of files successfully renamed.

=item B<-n>, B<-nono>

No action: print names of files to be renamed, but don't rename.

=item B<-f>, B<-force>

Over write: allow existing files to be over-written.

=item B<-h>, B<-help>

Help: print SYNOPSIS and OPTIONS.

=item B<-m>, B<-man>

Manual: print manual page.

=item B<-v>, B<-version>

Version: show version number.

=item B<-e>

Expression: code to act on files name.

May be repeated to build up code (like C).
If no B<-e>, the first argument is used as code.

=item B<-e>

Statement: code to act on files name, as B<-e> but terminated by ';'.

=back

=head1 ENVIRONMENT

No environment variables are used.

=head1 AUTHOR

Larry Wall

=head1 SEE ALSO

mv(1), perl(1)

=head1 DIAGNOSTICS

If you give an invalid Perl expression you'll get a syntax error.

=head1 BUGS

The original
C
did not check for the existence of target filenames,
so had to be used with care.
I hope I've fixed that (Robin Barker).

=cut

--------------

<-h><-m><-v><-v><-n><-f><-e><-e><-v><-verbose><-n><-nono><-f><-force><-h><-help><-m><-man><-v><-version><-e><-e><-e><-e>ubuntu 16.04 dailybiuld 2016/03/15での導入例

sudo apt-get install libutf8-all-perl libfile-rename-perl

パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています               
状態情報を読み取っています... 完了
注意、'libfile-rename-perl' の代わりに 'rename' を選択しています
rename is already the newest version (0.20-4).
The following additional packages will be installed:
  libimport-into-perl libmodule-runtime-perl libparams-classify-perl
提案パッケージ:
  libscalar-number-perl
以下のパッケージが新たにインストールされます:
  libimport-into-perl libmodule-runtime-perl libparams-classify-perl
  libutf8-all-perl
アップグレード: 0 個、新規インストール: 4 個、削除: 0 個、保留: 16 個。
57.9 kB のアーカイブを取得する必要があります。
この操作後に追加で 185 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://jp.archive.ubuntu.com/ubuntu xenial/main amd64 libparams-classify-perl amd64 0.013-5build1 [21.1 kB]
取得:2 http://jp.archive.ubuntu.com/ubuntu xenial/main amd64 libmodule-runtime-perl all 0.014-2 [15.6 kB]
取得:3 http://jp.archive.ubuntu.com/ubuntu xenial/main amd64 libimport-into-perl all 1.002005-1 [11.0 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu xenial/universe amd64 libutf8-all-perl all 0.017-1 [10.2 kB]
57.9 kB を 0秒 で取得しました (499 kB/s)   
以前に未選択のパッケージ libparams-classify-perl を選択しています。
(データベースを読み込んでいます ... 現在 214735 個のファイルとディレクトリがインストールされています。)
.../libparams-classify-perl_0.013-5build1_amd64.deb を展開する準備をしています ...
libparams-classify-perl (0.013-5build1) を展開しています...
以前に未選択のパッケージ libmodule-runtime-perl を選択しています。
.../libmodule-runtime-perl_0.014-2_all.deb を展開する準備をしています ...
libmodule-runtime-perl (0.014-2) を展開しています...
以前に未選択のパッケージ libimport-into-perl を選択しています。
.../libimport-into-perl_1.002005-1_all.deb を展開する準備をしています ...
libimport-into-perl (1.002005-1) を展開しています...
以前に未選択のパッケージ libutf8-all-perl を選択しています。
.../libutf8-all-perl_0.017-1_all.deb を展開する準備をしています ...
libutf8-all-perl (0.017-1) を展開しています...
man-db (2.7.5-1) のトリガを処理しています ...
libparams-classify-perl (0.013-5build1) を設定しています ...
libmodule-runtime-perl (0.014-2) を設定しています ...
libimport-into-perl (1.002005-1) を設定しています ...
libutf8-all-perl (0.017-1) を設定しています ...


cd ~/bin && cp `which rename` .

perl -pe "s/(^use\sPod::Usage;)(\n)/\1\nuse utf8::all;/" rename > renamej

実行属性をつけて、終わり。と思ったけど、コピーなので実行属性ついたままですね。