以下のようなスクリプトで、再帰的にファイルリストを取得しようとしています。
(WindowsXP上のActivePerl使用)
#----------------------------
#!/usr/bin/perl
$top_dir = "c:\\test";
use File::Find;
find( \&print_file_full_name, $top_dir );
sub print_file_full_name{
print $File::Find::name, "\n";
}
#----------------------------
これだと、「表」「予」「申」「能」「十」「ソ」などの0x5C文字がフォルダ名の最後に付いていると
そのフォルダより下を走査できません。
[例]¥日程表¥ ¥教育技能¥
File::Findを使用して全てのファイルを取得出来るようにする方法はありませんでしょうか。
(同程度のスピードが出れば File::Find を使用しなくても構いません。)
以下の質問の関連です。
http://q.hatena.ne.jp/1217570180
perl でできなくはないと思いますが、フォルダ名やファイル名に日本語が含まれている場合
どうしても文字コードの問題が付きまといます。
餅は餅屋ではないですが Windows であれば VBS 等、ファイルシステムとの親和性の高いものを
利用してはどうでしょうか。
perl から利用する場合は、コメントした通りです。
#!C:/Perl/bin/perl -w print system("CScript //nologo \"C:\\test\\listFile.vbs\" \"C:\\Test\\\"");
下記は、
>「第○階層まで」としたり、 depthLimit で指定
>「○○と付くフォルダは除外する」という指定 ignoreFolder で指定
を vbs で実装した例です。
【listFile.vbs】
Option Explicit '---------------------------------------- Const depthLimit = 3 '--- 処理をしないフォルダに含まれる文字を「,」区切りで列挙 Const ignoreFolder = "test,temp,log" '---------------------------------------- Dim fso, rootFolder, iFolders Set fso = CreateObject("Scripting.FileSystemObject") '--- 引数の確認 If WScript.Arguments.Count > 0 Then rootFolder = WScript.Arguments.item(0) Else '--- 指定がなければ C のルートを設定 rootFolder = "C:\" End If '--- フィルタ文字列を配列に変換 iFolders = Split( ignoreFolder, "," ) '--- 表示処理 printFolder 0, fso.getFolder( rootFolder ) '---------------------------------------- Sub printFolder( depth, objFolder ) '---------------------------------------- WScript.Echo objFolder.Path & "\" If depth >= depthLimit Then Exit Sub End If Dim sFolder, sFile '--- 指定の文字を含むフォルダは処理をしない For Each sFolder In iFolders If InStr( objFolder.Name, sFolder ) > 0 Then Exit Sub End If Next '--- フォルダ内のファイルの列挙 For Each sFile In objFolder.Files WScript.Echo sFile.Path Next '--- サブフォルダの処理 For Each sFolder In objFolder.SubFolders printFolder depth + 1, sFolder Next End Sub
インデントが見やすいのでコメントでなく回答させていただきましたが、
あくまで perl で処理を行うことが目的で、ということでしたらポイント不要です。
(他に回答なければキャンセルください。)
ファイル、フォルダのオプションを第二引数として追加した例です。
コマンド上で
CScript //nologo listFolder listFile.vbs ファイルパス [-folder | -file]
のように指定します。
Option Explicit '---------------------------------------- Const depthLimit = 3 '--- 処理をしないフォルダに含まれる文字を「,」区切りで列挙 Const ignoreFolder = "test,temp,log" '---------------------------------------- Dim fso, rootFolder, iFolders Set fso = CreateObject("Scripting.FileSystemObject") '--- 引数の確認 If WScript.Arguments.Count > 0 Then rootFolder = WScript.Arguments.item(0) Else '--- 指定がなければ C のルートを設定 rootFolder = "C:\" End If Dim mode mode = "BOTH" If WScript.Arguments.Count > 1 Then If UCase( WScript.Arguments.item(1) ) = "-FOLDER" Then mode = "FOLDER" End If If UCase( WScript.Arguments.item(1) ) = "-FILE" Then mode = "FILE" End If End IF '--- フィルタ文字列を配列に変換 iFolders = Split( ignoreFolder, "," ) '--- 表示処理 printFolder 0, fso.getFolder( rootFolder ) '---------------------------------------- Sub printFolder( depth, objFolder ) '---------------------------------------- If mode <> "FILE" Then WScript.Echo objFolder.Path & "\" End If If depth >= depthLimit Then Exit Sub End If Dim sFolder, sFile '--- 指定の文字を含むフォルダは処理をしない For Each sFolder In iFolders If InStr( objFolder.Name, sFolder ) > 0 Then Exit Sub End If Next '--- フォルダ内のファイルの列挙 If mode <> "FOLDER" Then For Each sFile In objFolder.Files WScript.Echo sFile.Path Next End If '--- サブフォルダの処理 For Each sFolder In objFolder.SubFolders printFolder depth + 1, sFolder Next End Sub
わざわざ機能増強していただきましてすみません。
週明けに試させていただきます。
-----------------------------------------------
[9.1追記]
少しテストしただけですが、とても高速ですね。ありがとうございます。
ただ、途中で読み取れないフォルダ(例えばアクセス権限がない)があるとその時点でエラーで止まってしまいます。
現在自作のPerlのスクリプトの方では読み取りエラーで処理出来なかった場合、処理出来なかったフォルダ・ファイルの一覧を別途吐き出すようにしています。
同様にエラーを取得して吐き出すにはどのようにすればいいでしょうか。
すいませんがよろしくお願いします。
------------------------------------------------
[さらに追記]
On Error Resume Next で飛ばしてエラーナンバーを拾って実装出来ました。
ありがとうございました。
------------------------------------------------
[終了のお礼]
スピードはFile::Findに到底かないませんでしたが、他に方法がなさそうなので今回はVBSを使用します。
ありがとうございました。
回答ありがとうございます。
目的が達せられるなら言語は問いません。
あと、結果にファイルだけ、フォルダだけ、両方、が選べたら完璧です :-)
週明けに試して再度返信させていただきます。