質問者
vbsでフォルダサイズを出すスクリプトがエラーする

質問
-
お世話になります。
下記スクリプトにて、ファイルサーバーの指定フォルダ配下の情報を出力したいのですが、
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
すべての返信
-
-
チャブーンです。
フルパスの文字数は問題ありませんでした。
ということですが、「50GB」のフォルダサイズを取得する際にはそのサブフォルダのファイルサイズの総計を再帰的に取得したうえひょうじされると思います。その意味で「もっとも最深のサブフォルダにあるファイルのフルパス文字列数」についても確認されたのでしょうか?
あと、同じ内容の投稿がWindows Server 2008フォーラムにされていますね。どちらかに統一していただき、片側を終了(ご自身で削除)していただいた方がよいと思います。
-
複数に分けて書き込めば良いだけでは。
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日 8:32 追加。
-
ご回答ありがとうございます。
ご指摘のありました投稿は削除しました。すいませんでした。
treeで表示して一番文字列の長いフルパスも確認しましたが、文字数は問題ありませんでした。
※<http://www2u.biglobe.ne.jp/~yuichi/rest/strcount.html>で確認追加ですが、「パスが見つかりません」のエラーは取り急ぎOn Error Resume Nextを付けて飛ばし、
エラーになったフォルダ情報は手動で確認することにしましたが、スクリプトが途中でエラー表示もなく終了してしまいます。csvを確認すると、止まっている箇所は毎回同じフォルダです。(50GB程)
On Error Resume Nextを付けており、該当のフォルダ名を取得した場合はFolder.sizeを抜くようにしても、
やはりエラーもなく止まります。該当フォルダをスルーしてスクリプトを終了するにはどうしたらいいでしょうか・・・
言語は問わないので、先輩方のお力を・・・
-
そういえば FolderItem.Size プロパティは Integer (IDLではLONG)なので32bit値です。50GBだとするとオーバーフローしています。
言語を問わないとのことですので、Windows APIなど別の手段を使用する必要があります。
-
「パスが見つかりません。」とのエラーは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にする方法があります。
-
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 追補を追加
-
失礼しました。改めて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社に修正要求するか。)
-
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 の書き方は不得手でして、裏取りして下さい。
- 編集済み ShiroYuki_Mot 2014年5月15日 3:12 改行挿入
-
ご呈示のスクリプトをPATHに「¥¥Localhost¥C$¥Users」を指定して実行してみたところ、確かに止まってしまいますね。
Sub Query_Directory(PATH,i)
の直後に
On Error Resume Next
を入れて実行してみると、システムの隠しフォルダやシンボリックリンクもたどっているようでした。
また上記Resumeがない状態では、最初のシンボリックリンクであるAll Usersで止まっていました。
もしかしたら、シンボリックリンク周りが関係しているかもしれません。ループしていたりして260文字のパスを超えてしまっていたり。020と021の間に、Treeで表示されない隠しフォルダが0210とかが存在したり。ためしに上記コードを入れてみて、取れていないものや、意図していないパスをたどっていたりしないか確認してみてはどうでしょうか。
以上、参考まで。- 編集済み やき(Yaki) 2014年5月19日 1:43
-
やき さま 拝見しました。
本ご質問とは無関係と思われますが、参考でご覧戴く方の為に。
シンボリックリンクやジャンクション そして フォルダ名の長さ 更に 隠しフォルダは、
恐らく、無関係と思っています。 今回のケースでは。
お試しの、ユーザー絡みのフォルダで失敗する原因は、アクセス権が取得出来ないからです。
これらのフォルダは、OS 直轄の特殊フォルダであり、一般的にはアクセスさせなくしてあります。
例えば、デバッグが可能な Visual Basic 等で試すと、以下の様な警告がされます。
パス 'c:\Users\All Users\Application Data' へのアクセスが拒否されました。 あるいは、 パス 'c:\Users\XXX\AppData\Local\Application Data' へのアクセスが拒否されました。 このリソースにアクセスするのに十分な」権限があることを確認します。
参考までに、今回の事象は、佐祐理さまのご投稿でご指摘の様に、
VARIANT型で定義された変数が、int32>int64>int32と戻り値を取る時、
拡大変換へは移行出来ても、縮小変換に戻る時(フォルダ 020>021)に、
エラーを引き起こしているのであろうと思っています。
この仮定があっている限り、VBS では難しいかな と。 -
皆様ご回答ありがとうございます。泣
ご回答頂いた内容を一通り試してみましたが、やはり同じフォルダで停止してしまいます・・・
上長へ確認したところ、以前からファイルパスの長さが原因でファイルコピーが出来なかったりしていたらしく、
やはり最下層のファイルパスが長すぎなのかなと・・・下の階層を見に行く毎に現在の階層で仮想ドライブを張り直す、といった具合にすれば、
パスの長さが原因でとまることはないのかなと、スクリプトを作り直してます。(\\fileserver01\share)(Y:)を割り当て、サブフォルダを取得
↓
(\\fileserver01\share)(Y:)を切断
↓
(\\fileserver01\share\subfolder01)(Y:)を割り当て、配下を取得
↓
(\\fileserver01\share\subfolder01)(Y:)を切断
↓
(\\fileserver01\share\subfolder01\subfolder001)(Y:)を割り当て、サブフォルダを取得・・・といった具合に、Y:ドライブに割り当ててからサブフォルダを取得していくようなイメージですが、
どうもうまくいかず・・・ -
ご回答ありがとうございます。
cmdですと「ディレクトリ名 \\hogehoge\hoge\・・・・は長すぎます。」と表示され容量を計算できません。
-
- 編集済み acchan0229 2014年5月20日 6:18
-
ロングパスが問題であれば、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では特に問題おきませんでした。失礼しました。以上、参考まで。
-
ご回答ありがとうございます。
ご教示頂きました< \\?\UNC\server01\share\Folder01>の表記に変え実施してみましたが、変わらずでした。
事象がかわらなかった(エラーせず終了してしまう)ことから、サポートしていない訳ではなさそうですが・・・
パスが長いファイルが取得出来ず停止してしまっているため、サブフォルダを取得する毎に、
パスをネットワークドライブに割り当てながら最下層まで行ければ原因を避けることは可能なのではなかと思います。が、スクリプトの作成が難航している状況なので、まだ時間がかかりそうです・・・
-
ご回答ありがとうございます。
dir /S \\?\Z:\
で実行した結果、途中から「ディレクトリ名\\?\Z:\hogehoge~は長すぎます。」とメッセージが表示されます。
処理は進みますが、やはり該当のフォルダは結果が出力されません。(しかも、結構なフォルダ数・・・)
最初にフォルダ一覧を出力、そのフォルダ名を代入、一つずつdirで取得、容量を追記するようにしましたが、
やはり止まっちゃいます。。。 -
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
Loopresultfile.WriteLine deps & f.path & " " & f.DateLastModified & " " & strLine
Next
End Sub
-
PowerShell で dir -s とかした場合は、正しく表示されるのでしょうか?
問題なく出力されるのであれば
dir -rec |%{if((Get-Item $_.fullname).PSISContainer){[string]@(dir $_.fullname).length + " " + $_.Fullname}}
とかで代用できるのでは。
(Get-ChildItem hogehoge).Lengthに注意 - PowerShell Scripting Weblog -
同様に組み合わせれば良いだけでは。
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