none
vbsでフォルダサイズを出すスクリプトがエラーする RRS feed

  • 質問

  • お世話になります。

    下記スクリプトにて、ファイルサーバーの指定フォルダ配下の情報を出力したいのですが、
    csv出力が途中まで進んでエラーして止まってしまいます。

    エラーする箇所(フォルダ)は毎回同じで、フォルダサイズだけスクリプトから外すと正常に出力されることから、
    恐らく検索しているフォルダのサイズが大きすぎることが原因なのではないかと思っています。

    2GB程度のフォルダは正常に出力されますが、毎回止まるフォルダのサイズは50GB程あります。

    どのようにすれば、フォルダサイズも含め全て出力出来るかご教授頂けないでしょうか。

    ↓エラー内容

    スクリプト:
    行:53
    文字:1
    エラー:パスが見つかりません。
    コード:800A004C
    ソース:Microsoft VBScript実行時エラー

    ↓スクリプト

    '------------------------------' csv作成 '------------------------------

    On Error Resume Next

    'csv出力先フォルダ 
    RESULT_DIR = "D:"

    'FileSystemObject生成
    Set fso = CreateObject("Scripting.FileSystemObject")

    'CSV作成
    Set resultfile = fso.CreateTextFile(RESULT_DIR & "\" & "result.csv",True)
    resultfile.WriteLine deps & "PATH" & "," & "FolderName" & "," & "Size" & "," & "LastModified"

    'Sub呼び出し
    Call Query_Directory("\\fileserver01\share",1)

    '------------------------------' 検索Sub '------------------------------
    Sub Query_Directory(PATH,i)

    Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
    Set objFOLDERS = objFSO.GetFolder(PATH)

    'フォルダがあればフォルダの中をさらに展開
    For Each Folder In objFOLDERS.SubFolders

    'フォルダ情報出力(パス、容量、最終更新日)
    'resultfile.WriteLine Folder.path & "," & Folder.name & "," & Round(Folder.size /1048576, 2) & "MB" & _
    '"," & DateValue(Folder.DateLastModified)

    '↑Folder.sizeを外すと正常終了します

    If i > 0 Then
    Call Query_Directory(Folder,i-1)
    End If

    Next

    Set objFSO = Nothing
    Set objFOLDERS = Nothing
    End Sub

    2014年5月10日 8:50

すべての返信

  • PowerShellと無関係です。ご自分の使用されている言語が何かを把握すべきです。

    エラー内容が「パスが見つかりません。」とのことですから、原因はフォルダーサイズではなく、パス名が異常なのでしょう。Windowsのパス名には260文字の制限があります。この制限に引っかかっている可能性はありませんか? エラーの起きた具体的なパスを確認してみてください。

    もし260文字制限に引っかかった場合、別のアクセス方法が必要になります。ただしScripting.FileSystemObjectがその方法をサポートしているかどうかまではわかりません。

    2014年5月10日 10:02
  • ご回答ありがとうございます。

    言語が選べなかったので取り急ぎPowerShellを選んだ次第です。

    フルパスの文字数は問題ありませんでした。

    2014年5月10日 11:25
  • チャブーンです。

    フルパスの文字数は問題ありませんでした。

    ということですが、「50GB」のフォルダサイズを取得する際にはそのサブフォルダのファイルサイズの総計を再帰的に取得したうえひょうじされると思います。その意味で「もっとも最深のサブフォルダにあるファイルのフルパス文字列数」についても確認されたのでしょうか?

    あと、同じ内容の投稿がWindows Server 2008フォーラムにされていますね。どちらかに統一していただき、片側を終了(ご自身で削除)していただいた方がよいと思います。

    http://social.technet.microsoft.com/Forums/ja-JP/887c770f-36f6-48a9-be2b-a1196ac5192b/vbs?forum=windowsserver2008ja

    2014年5月11日 7:43
  • 複数に分けて書き込めば良いだけでは。

    resultfile.Write Folder.path & ","
    resultfile.Write Folder.name & ","
    resultfile.Write Round(Folder.size /1048576, 2) & "MB,"
    resultfile.Write DateValue(Folder.DateLastModified) & vbNewLine

    確認はしていませんが、空のフォルダが 0 バイトになるような気がします・・・
    2014年5月12日 2:05
  • ご回答ありがとうございます。

    ご指摘のありました投稿は削除しました。すいませんでした。

    treeで表示して一番文字列の長いフルパスも確認しましたが、文字数は問題ありませんでした。
    ※<http://www2u.biglobe.ne.jp/~yuichi/rest/strcount.html>で確認

    追加ですが、「パスが見つかりません」のエラーは取り急ぎOn Error Resume Nextを付けて飛ばし、
    エラーになったフォルダ情報は手動で確認することにしましたが、スクリプトが途中でエラー表示もなく終了してしまいます。

    csvを確認すると、止まっている箇所は毎回同じフォルダです。(50GB程)

    On Error Resume Nextを付けており、該当のフォルダ名を取得した場合はFolder.sizeを抜くようにしても、
    やはりエラーもなく止まります。

    該当フォルダをスルーしてスクリプトを終了するにはどうしたらいいでしょうか・・・

    言語は問わないので、先輩方のお力を・・・

    2014年5月14日 10:09
  • ご回答ありがとうございます。

    ご認識の通り、空のフォルダは0バイトで出力されますが、正常な結果です。

    書き込みを複数に分けましたが、結果は同じでした・・・

    2014年5月14日 10:10
  • そういえば FolderItem.Size プロパティは Integer (IDLではLONG)なので32bit値です。50GBだとするとオーバーフローしています。

    言語を問わないとのことですので、Windows APIなど別の手段を使用する必要があります。

    2014年5月14日 11:49
  • 「パスが見つかりません。」とのエラーは53行目で発生しているようですが、ご提示のスクリプトは40行程度のようです。

    実際は他のエラーを拾っている可能性はないでしょうか?

    VBScript+FileSystemObjectでこの手のスクリプトを実行しエラーが発生するよくあるパターンとしては以下の2点があります。

    1. スクリプトを実行するアカウントにフォルダーへのアクセス権がない

    この場合、objFOLDERS.SubFoldersでサブフォルダーの一覧を取得する段階で「書き込みできません」とのアクセス拒否エラーが出て終了するかと思います。

    対処方法としては、スクリプトを実行するアカウントにフォルダーへのアクセス権を付与する、もしくはフォルダーへのアクセス権を有するアカウントでスクリプトを実行する以外にないと思います。

    2.Shift_JISで表現できない文字をフォルダ名に使用している

    残念ながらFileSystemObjectのTextStreamでは、Shift_JISで文字列を出力する場合、仮に出力文字が一つでもShift_JISの文字コードに含まれておらず表現不能であった場合は、出力そのものが失敗しエラーとなってしまいます。

    よくあるエラーとなる文字の例としては、Unicodeの記号等があります。「☺」とかですね。あとは「-」に似た「⁻」等の文字が含まれていてエラーになるというのがありがちです。

    この場合、該当するフォルダーのプロパティを書きだす際、resultfile.WriteLine実行時に「プロシージャの呼び出し、または引数が不正です。」とのエラーで終了すると思います。

    対処方法としては、フォルダ名をあらかじめShift_JISで表現可能な文字のみにリネームしておく手があります。

    もしくは、

    Set resultfile = fso.CreateTextFile(RESULT_DIR & "\" & "result.csv",True, True)

    として出力テキストファイルの文字コードをUnicodeにする方法があります。

    2014年5月14日 12:12
    モデレータ
  • acchan0229 さま よろしく。

    気になったので、書き込みます。

    VB は分りますが、VBS は弄った事がないので、勘 ですが。
    Folder.size がオーバーフローしているのではありませんか。
    VBS で、size パラメータの仕様を調べたのですが、見付けられませんでした。
    仮に、Integer(Int32) とすると、上限は 2,147,483,647 符号なしでは 4,294,967,295。
    他方、50GB は 53,687,091,200、2GB は 2,147,483,648。

    因みに、VB そのものでは、FileInfo.Length プロパティで Int64 です。

    スクリプトを、エラーを起こすフォルダのみに対して動作するように書き換え、
    Folder.size がちゃんと答えを返すか見て下さい。

    オーバーフローしているとすれば、その後、正常に動作していない事も考えられますね。
    その場合、VBS では、不可能な作業 という事になりますね。

    [追補] 投稿後、佐祐理さまのご投稿を発見しました。 見落としました。 重複しますね。 やはり、Int32 ですか。

    • 編集済み ShiroYuki_Mot 2014年5月14日 13:07 追補を追加
    2014年5月14日 13:00
  • ご回答ありがとうございます。

    1)アクセス権はあり、全フォルダ閲覧可能です。

    2)ご指摘の通りUnicodeで出力しましたが、同じ箇所で止まっていました・・・

    2014年5月14日 23:47
  • ご回答ありがとうございます。

    確認した内容です。

    ・<\\fileserver01\share>配下のフォルダ「001」~「100」まで全て取得しようとすると
     毎回「020」まで出力してスクリプトが停止しています。(エラーなし)

    ・「020」、「021」のみスクリプトを実施すると正常に出力されます。

    ・「020」は50GB、「021」は10GB

    せめて、「021」のみ飛ばして全て取得できればいいのですが、ifで「021」のSIZEはいらないってしても、
    やはり同じ箇所で止まります・・・

    2014年5月15日 1:03
  • ご回答ありがとうございます。

    フォルダは「001」~「100」まであり、毎回「020」まで出力した状態でスクリプトが終了しています。

    「020」は50GB程でフォルダプロパティの表示とほぼ同じ数値を正常に出力、それに対し「021」は、
    10GB程度なのになにも出力されません。

    「020」が出力されずに停止していたならオーバーフローと断定出来るのですが・・

    2014年5月15日 1:57
  • 失礼しました。改めてVS2013上で「DateLastModified」プロパティを検索しましたがヒットしませんでした。Scripting.FileSystemObjectは開発者向けにIDLが公開されていないオブジェクトですね。

    Size Propertyが正解でした。しかしサイズ情報が書かれていないのでTLBを確認したところ、

    [id(0x000003f1), propget, helpstring("Sum of files and subfolders"), helpcontext(0x00214b70)]
    HRESULT Size([out, retval] VARIANT* pvarSize);
    

    とVARIANT型なので何が返ってくるかは実行時にしかわからないようでした。

    ともあれ、エラーになって困っているのであれば、別の方法をとるしかないと思います。(もしくはMicrosoft社に修正要求するか。)

    2014年5月15日 2:42
  • FileSystemObjectのIFolder::SizeはVARIANTで、2^31-1までならVT_I4(=Long)で、それ以上ならVT_R8(=Double)で返してくるみたいですよ。
    2014年5月15日 2:48
  • acchan0229 さま 拝見しました。

    あれぇ、単独では、オーバーフローせず、取得出来ているんですか。
    という事は、INT32 や UINT32 ではなく、INT64 辺りで帰って来ている事になりますね。
    (こりゃぁ、まいったなぁ。 よう、わからん。)

    後は、暗黙の変換が適応されている箇所、

    & Round(Folder.size /1048576, 2) & "MB" & _ を
    & CSTR(Round(Folder.size /1048576, 2)) & "MB" & _ の様に、
    ちゃんと、形変換してやるとか、

    Set sizeBasedB = 0
    Set sizeBasedB = Folder.size
    Set sizeBasedMB = 0.00
    Set sizeBasedMB = Round(sizeBasedB /1048576, 2)
    Set sizeBasedMBStrings = CSTR(sizeBasedMB)
    resultfile.WriteLine Folder.path & "," & Folder.name & "," & sizeBasedMBStrings & "MB" & _


    の様に、変数の初期化を挟む位しか思い当たりません。

    尚、VBS の書き方は不得手でして、裏取りして下さい。

    2014年5月15日 3:09
  • ご呈示のスクリプトをPATHに「¥¥Localhost¥C$¥Users」を指定して実行してみたところ、確かに止まってしまいますね。

    Sub Query_Directory(PATH,i)

    の直後に

    On Error Resume Next

    を入れて実行してみると、システムの隠しフォルダやシンボリックリンクもたどっているようでした。
    また上記Resumeがない状態では、最初のシンボリックリンクであるAll Usersで止まっていました。


    もしかしたら、シンボリックリンク周りが関係しているかもしれません。ループしていたりして260文字のパスを超えてしまっていたり。020と021の間に、Treeで表示されない隠しフォルダが0210とかが存在したり。

    ためしに上記コードを入れてみて、取れていないものや、意図していないパスをたどっていたりしないか確認してみてはどうでしょうか。


    以上、参考まで。


    2014年5月19日 1:39
  • やき さま 拝見しました。

    本ご質問とは無関係と思われますが、参考でご覧戴く方の為に。

    シンボリックリンクやジャンクション そして フォルダ名の長さ 更に 隠しフォルダは、
    恐らく、無関係と思っています。 今回のケースでは。
    お試しの、ユーザー絡みのフォルダで失敗する原因は、アクセス権が取得出来ないからです。
    これらのフォルダは、OS 直轄の特殊フォルダであり、一般的にはアクセスさせなくしてあります。
    例えば、デバッグが可能な Visual Basic 等で試すと、以下の様な警告がされます。
    パス 'c:\Users\All Users\Application Data' へのアクセスが拒否されました。 あるいは、
    パス 'c:\Users\XXX\AppData\Local\Application Data' へのアクセスが拒否されました。
    このリソースにアクセスするのに十分な」権限があることを確認します。

    参考までに、今回の事象は、佐祐理さまのご投稿でご指摘の様に、
    VARIANT型で定義された変数が、int32>int64>int32と戻り値を取る時、
    拡大変換へは移行出来ても、縮小変換に戻る時(フォルダ 020>021)に、
    エラーを引き起こしているのであろうと思っています。
    この仮定があっている限り、VBS では難しいかな と。
    2014年5月19日 9:46
  • 皆様ご回答ありがとうございます。泣

    ご回答頂いた内容を一通り試してみましたが、やはり同じフォルダで停止してしまいます・・・

    上長へ確認したところ、以前からファイルパスの長さが原因でファイルコピーが出来なかったりしていたらしく、
    やはり最下層のファイルパスが長すぎなのかなと・・・

    下の階層を見に行く毎に現在の階層で仮想ドライブを張り直す、といった具合にすれば、
    パスの長さが原因でとまることはないのかなと、スクリプトを作り直してます。

    (\\fileserver01\share)(Y:)を割り当て、サブフォルダを取得

    (\\fileserver01\share)(Y:)を切断

    (\\fileserver01\share\subfolder01)(Y:)を割り当て、配下を取得

    (\\fileserver01\share\subfolder01)(Y:)を切断

    (\\fileserver01\share\subfolder01\subfolder001)(Y:)を割り当て、サブフォルダを取得・・・

    といった具合に、Y:ドライブに割り当ててからサブフォルダを取得していくようなイメージですが、
    どうもうまくいかず・・・

    2014年5月20日 0:03
  • なぜ「言語は問わない」書かれていながらvbsに拘るのでしょうか?
    2014年5月20日 0:19
  • PowerShellでも試しましたが、やはりエラーしてしまうのと、
    そもそもあまり慣れていないからです・・・泣

    2014年5月20日 1:23
  • 一番簡単で速いのは、コマンドプロンプトから dir /S で取得する事ですね。
    2014年5月20日 2:00
  • ご回答ありがとうございます。

    cmdですと「ディレクトリ名 \\hogehoge\hoge\・・・・は長すぎます。」と表示され容量を計算できません。

    2014年5月20日 3:47
  • MAX_PATH を超える場合は UNC パスを使う事で FileSystemObject を利用してアクセス出来るかもしれません。

    \\?\ をパスの先頭につけて、実行してみてください。(未検証です)

    Naming Files, Paths, and Namespaces (Windows)


    • 編集済み 藤森幸治 2014年5月20日 7:58 リンクの追加
    2014年5月20日 4:25
  • ご回答ありがとうございます。

    \\server01\share\Folder01>のように指定してNGです。

    ネットワークドライブに割り当ててもNGです。


    2014年5月20日 6:17
  • ロングパスが問題であれば、Robocopyの結果からフォルダサイズを取得するという手があるようです。
    Powershellとの組み合わせで抽出している方がいました。

    Get Folder Size when path is too long
    http://social.technet.microsoft.com/Forums/scriptcenter/en-US/30efc904-0aea-454e-a8d0-60408258126e/get-folder-size-when-path-is-too-long?forum=ITCG


    robocopy "large path name" "c:\temp" /zb /e /l /r:1 /w:1 /ndl /nfl /bytes /np /njh /log:size.log

    このコマンドのうち、/L オプションで、実際には出力させないようにしています。
    /log オプションを除けば、コマンドラインで出力が確認できます。

    日本語環境であれば「バイト」に続くのがフォルダのサイズのようです。

    p.s.
    シンボリックリンクのループはDir /S では同様のエラーが出ましたが、いただいたvbsでは特に問題おきませんでした。失礼しました。

    以上、参考まで。

    2014年5月20日 8:57
  • UNCパスの場合は以下のような表記をするようです。

    \\?\UNC\server01\share\Folder01

    2014年5月22日 10:11
    モデレータ
  • 回答ではありませんが、「長すぎます。」というエラーが出るのであればやはり最初に書いた「もし260文字制限に引っかかった場合、別のアクセス方法が必要になります。」という指摘が当たっていたと思われますね。

    ここで触れていた「別のアクセス方法」というのが藤森幸治さん、牟田口大介さんの挙げられている \\?\ を付与するアクセス方法のことであり、「ただしScripting.FileSystemObjectがその方法をサポートしているかどうかまではわかりません。」という記述が藤森幸治さんの「(未検証です)」に相当します。

    2014年5月23日 2:57
  • ご回答ありがとうございます。

    ご教示頂きました< \\?\UNC\server01\share\Folder01>の表記に変え実施してみましたが、変わらずでした。

    事象がかわらなかった(エラーせず終了してしまう)ことから、サポートしていない訳ではなさそうですが・・・

    パスが長いファイルが取得出来ず停止してしまっているため、サブフォルダを取得する毎に、
    パスをネットワークドライブに割り当てながら最下層まで行ければ原因を避けることは可能なのではなかと思います。

    が、スクリプトの作成が難航している状況なので、まだ時間がかかりそうです・・・

    2014年5月23日 11:12
  • 一度ネットワークパスを Z ドライブに割り当てて

    dir /S \\?\Z:\ 

    エラーが出ないようなら、そのパスを使って、VBS で試してみてください。

    2014年5月26日 1:19
  • ご回答ありがとうございます。

    dir /S \\?\Z:\

    で実行した結果、途中から「ディレクトリ名\\?\Z:\hogehoge~は長すぎます。」とメッセージが表示されます。

    処理は進みますが、やはり該当のフォルダは結果が出力されません。(しかも、結構なフォルダ数・・・)

    最初にフォルダ一覧を出力、そのフォルダ名を代入、一つずつdirで取得、容量を追記するようにしましたが、
    やはり止まっちゃいます。。。

    2014年5月26日 9:47
  • dirコマンドではファイルパスが長すぎるとエラーしますが処理は進むので、コマンドにフォルダパスを渡しながらテキストに落とし込んでいくスクリプトを書きました。

    が、やはり止まります。

    ログを見ると、dirは正常に終了しており、容量も出せていましたが、その値を書き込む段階でスクリプトが停止しています。

    いくつか書き込み出来ているのに、必ず同じフォルダで終了してしまいます。(そのフォルダは100GBを超えています)

    容量の計算はコマンドで実行して結果は正常に出ており、その結果をvbsで出力しているだけなのに、なぜ停止してしまうのか分かりません。

    どうか、ご教授頂けないでしょうか。

    '------------------------------' csv作成 '------------------------------
    SEARCH_DIR = \\server01\share\

    'csv出力先フォルダ
    RESULT_DIR = "D:\"

    'FileSystemObject生成
    Set fso = CreateObject("Scripting.FileSystemObject")

    'CSV作成
    Set resultfile = fso.CreateTextFile(RESULT_DIR & "\RESULT.txt",true)

    'サブメソッド呼び出し
    Call Sub_SearchDir(SEARCH_DIR,"")

    WScript.Echo("END1")

    set branch = nothing
    set fso = nothing

    '------------------------------' 検索サブ '------------------------------
    sub Sub_SearchDir(branchpath, deps)

    '変数
    Set objShell = WScript.CreateObject("WScript.Shell")
    Dim fsoFolder,fsoSubFolder,fsoFile,WshShell
    Set WshShell = CreateObject("WScript.Shell")

    'フォルダオブジェクト取得
    Set fsoFolder = fso.GetFolder(branchpath)

    'サブフォルダ検索
    Set branch = fso.getfolder(branchpath)

    'On Error Resume Next

    'コマンド
    cmd = "cmd /c dir /s /a-d "
    file = """総数 ファイル"""
    file2 = """*.*"""
    find = " | findstr "
    findv = " | findstr /v "
    batlog = " >D:\log.txt 2>&1"

    'SEARCH_DIR配下のフォルダを検索
    For each f in fsoFolder.subfolders

    'コマンド実行
    set objExec = objShell.Exec(cmd & Chr(34) & f.path & Chr(34) & find & file & findv & file2)

    '標準出力が終了するまでループ
     Do Until objExec.StdOut.AtEndOfStream
     strLine = objExec.StdOut.ReadLine
     Loop

    resultfile.WriteLine deps & f.path & " " & f.DateLastModified & " " & strLine

    Next

    End Sub

    2014年6月5日 2:22
  • PowerShell で dir -s とかした場合は、正しく表示されるのでしょうか?

    問題なく出力されるのであれば

    dir -rec |%{if((Get-Item $_.fullname).PSISContainer){[string]@(dir $_.fullname).length + "   " + $_.Fullname}}
    とかで代用できるのでは。
    (Get-ChildItem hogehoge).Lengthに注意 - PowerShell Scripting Weblog

    2014年6月5日 7:00
  • ご回答ありがとうございます。

    ご教示頂いた内容でエラーはしませんでしたが各フォルダのサイズが出せませんでした。

    もともとPowerShellだと各フォルダのサイズが出せないため試行錯誤しましたが、
    PowerShellが苦手ということもあり、vbsとコマンドに頼っている次第です。。。

    2014年6月6日 5:12
  • 同様に組み合わせれば良いだけでは。

    dir -rec |%{if((Get-Item $_.fullname).PSISContainer){[string]@(dir $_.fullname).length + "   " + $_.Fu
    llname + "   " + [decimal]("{0:N2}" -f ((Get-ChildItem $_.FullName | where Length | measure Length -sum).sum / 1MB))}}
    PowerShell で フォルダの容量一覧を取得したい - tech.guitarrapc.cóm

    2014年6月6日 7:26