人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

Perlでファイルリストを出来るだけ高速に取得する方法を教えてください。

以下のようなスクリプトで、再帰的にファイルリストを取得しようとしています。
(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

●質問者: harrypotter
●カテゴリ:コンピュータ
✍キーワード:name Perl print sub test
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ● Mook
●35ポイント

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 で処理を行うことが目的で、ということでしたらポイント不要です。

(他に回答なければキャンセルください。)

◎質問者からの返答

回答ありがとうございます。

目的が達せられるなら言語は問いません。

あと、結果にファイルだけ、フォルダだけ、両方、が選べたら完璧です :-)


週明けに試して再度返信させていただきます。


2 ● Mook
●35ポイント

ファイル、フォルダのオプションを第二引数として追加した例です。

コマンド上で

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を使用します。

ありがとうございました。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ