- ベストアンサー
(VBA)bat処理の結果がおかしい
- VBAを使用して、(VBA)bat処理の結果が正しくないという問題があります。
- 具体的には、指定ディレクトリにBATファイルをコピーし、フォルダーを削除してからフォルダー名を変更する処理が正しく動作しないようです。
- 原因を調査して修正方法を教えていただきたいです。
- みんなの回答 (21)
- 専門家の回答
質問者が選んだベストアンサー
いつもながらの思い付きですが 'batファイルの起動 の前に ChDir mypath にしてみるとか
その他の回答 (20)
- kkkkkm
- ベストアンサー率66% (1725/2595)
> \temp\AAを削除するコードを教えて下さい。 BATはよくわかりませんが、rd "%%i"で削除されることもないのですね。 AAがAA_AAになるのでしたらフォルダ一覧取得して If InStr(フォルダ名, "_") > 0 Then 削除対象フォルダ名 = Left(フォルダ名, InStr(フォルダ名, "_") - 1) End If で削除対象フォルダ名が取得できます。
お礼
アドバイス、感謝いたします。 >BATはよくわかりませんが、rd "%%i"で削除されることもないのですね。 そうです。 指摘されてるように Bat的には、削除されるはずなのに削除されない状態です。 教えてもらったコードを利用して 下記にように変更してみましたがエラーが発生します オブジェクト変数または With ブロック変数が設定されていません。(エラー番号:91) コードの挿入歌所が不適切なのが原因でしょうか ? -------------------------------------------------------------- 'フォルダー内のフォルダー名 FolderPath = MyPath '--- 含まれるフォルダの数を知りたいフォルダのパス ---' '--- ファイルシステムオブジェクト ---' Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") 'フォルダー内のフォルダーを書き出す Set MyF = fso.GetFolder(FolderPath) ' 含まれるフォルダ名を知りたいフォルダを返します。 IntR = 1 For Each SubF In MyF.SubFolders 'サブフォルダーを取得します。 '追加分 --------------------- ここから If InStr(SubF, "_") > 0 Then MsgBox SubF DelSubF = Left(SubF, InStr(SubF, "_") - 1) Kill MyPath & DelSubF End If ’追加分 ---------------------- ここまで Cells(IntR, "A") = SubF.Name IntR = IntR + 1 Next Set fso = Nothing Set MyF = Nothing
補足
すいません。 エラーが出るのは、下記コードです。 DelSubF = Left(SubF, InStr(SubF, "_") - 1)
- kkkkkm
- ベストアンサー率66% (1725/2595)
> 直前で宣言しても良いのかな?と思っているのですが..... どうでしょう。リンクしていただいたサイトを見ても、私にとってはあまり納得できる材料がない感じです。 > 変数名 どの行番号で使われているか? Visual Studioだとコンパイルの時に利用していない変数があると注意してくれますが、VBAには利用している場所や未使用注意も無いと思います(多分) > 変数MyNoとすべきを間違ってMyNooとミスった場合 > 変数の間違いが判りやすい 宣言を強制していればタイプミスした場合、実行時にエラーになるのでわかると思います。
補足
回答ありがとうございます。 了解しました。 私が思っている変数リストのような機能はVBAには無いのですね。 >宣言を強制していればタイプミスした場合、実行時にエラーになるのでわかると思います。 これから、モジュールの先頭に「Option Explicit」を記述するようにします。 ----------------------------------- 一応、コードの見直しも終わらせて 実際のDATAで処理を行ったところ 処理手順で不具合があり、BAT後に追加処理を挿入する事になりました。 追加処理とは、 バッチ後にバッチ前と同じフォルダー名のフォルダーを削除する です。 以下の参考画像を例にすると https://imgur.com/uaUvw6m BAT後のBAT前と同じ名前の\temp\AAを削除です。 (\temp\AA内にAA.RARは必要無いし \temp\AA事体を以後のVBAでの処理から外したい) \temp\AAを削除するコードを教えて下さい。 処理すべきフォルダーが10個ある場合は、 削除対象のフォルダーは同じく10個存在します。
- kkkkkm
- ベストアンサー率66% (1725/2595)
> 「同期の場合(バッチファイルの結果をVBAで待つ場合)」の下記に変えてみました。 > Call obj.Run(sPath, WaitOnReturn:=True) そういうのがあるのですね。 > 修正すべき点あればお願いします。 かなり長いので、ぱっと見た目だけですが MyName = Dir(mypath, vbDirectory) ' 最初のフォルダ名を返します。 '--- フォルダ数を格納する変数 ---' Dim n As Long CountFolder = fso.GetFolder(folderPath).SubFolders.Count の2か所はいらないような気がします。 > 変数宣言(DIM)は、普通最初にまとめて宣言 その方がいいと思います。途中で宣言すると同じ変数名を利用してしまう可能性があります。実行するまでエラーが出ないので面倒です。
補足
修正点の指摘ありがとうございます。 現在、コードの修正を行っています。 (setをNothingしておくなど追加中) 実は、ネットで下記のような記事を見つけて 直前で宣言しても良いのかな?と思っているのですが..... https://hatena19.com/position-of-variable-declaration-in-vba/ 話は、全く違うのですがご存じなら教えて下さい。 昔々、1970年代のBASIC言語を使用していたとき 変数名の間違いをチェックするのに どこでその変数が使われているかチェックするプログラムがありました。 (先人の堪能者が作成して雑誌等にも亜種も含めて紹介されていました。) 変数名 どの行番号で使われているか?がABC順に一覧リスト化されて表示される。 例えば、 MyNo 40,120,15 MyNoo 30 MyNoは、行番号 40,120,125 で利用されている。 で、変数MyNoとすべきを間違ってMyNooとミスった場合 変数の間違いが判りやすいと言うプログラムです。 -------------------------- EXCELのVBAでも上記のような機能やプログラムはあるのでしょうか?
- kkkkkm
- ベストアンサー率66% (1725/2595)
> BAT処理が完全に終わるのを待つ必要があるように思えます。 待つ処理は以下のサイトを参考にしてみてください。 VBA Sleep関数について、同じような機能を持つ他の関数やメソッドについても紹介 https://it-kyujin.jp/article/detail/1843/
補足
解説ありがとうございます。 試しに、最初のバッチの起動を 「同期の場合(バッチファイルの結果をVBAで待つ場合)」の下記に変えてみました。 Call obj.Run(sPath, WaitOnReturn:=True) 結果、stopを入れなくても処理が上手くできるようになりました。 一応、以下のコードで処理自体はできましたが、 修正すべき点あればお願いします。 ------------------------------ 私のコードは、ネットのコードを修正したり 過去教えてもらったコードで再利用できそうなコードの組み合わせだと 以前書きましたが、 そこでお聞きしたいのですが 変数宣言(DIM)は、普通最初にまとめて宣言(記載)しまうが これが途中にある場合は、不都合がありますか。 (もちろん、コードの順番では変数を利用する前の上方にはあります。) 今は、使いまわしのコードでdimがある場合は 最初のsub()の直下になるように書き換えています。 (最初に「Option Explicit」を付けない場合は そもそも変数宣言は必要ないがエラー修正が難しくなるので 宣言は行うようにはしています。) ------------------------------------------------------ Sub MooveUp_Directory() Dim strPath As String Dim intPathLen As Integer Dim intR As Integer Dim F As Variant Dim obj As WshShell Dim sPath As String Dim folderPath As String Dim CountFolder As Single Dim strFlName As String Dim SubF As Object Dim MyF As Object Dim ws01 As Worksheet Dim lRow As Single Dim FolderName, OldFile, NewFile As String Dim MojiSuu As Single Dim KokoKara As Variant Dim I As Single Dim Nukidashi As String Dim EndRow As Single 'Range("A5:F100").Clear ' フォルダーを自由に選べること。 参考:officeTANAKA With Application.FileDialog(msoFileDialogFolderPicker) If .Show = True Then If Len(.SelectedItems(1)) = 3 Then ' c:\の場合とサブフォルダーの場合 mypath = .SelectedItems(1) Else mypath = .SelectedItems(1) & "\" End If End If End With If mypath = Empty Then MsgBox "フォルダー名の指定をキャンセルしました。": Exit Sub MyName = Dir(mypath, vbDirectory) ' 最初のフォルダ名を返します。 'Range("A2") = mypath 'コピー先フォルダーの指定 'BATファイルのコピー FileCopy "C:\MoveUp_Directory.bat", mypath & "MoveUp_Directory.bat" 'batファイルの起動 sPath = mypath & "MoveUp_Directory.bat" 'MsgBox mypath Set obj = New WshShell ChDir mypath Call obj.Run(sPath, WaitOnReturn:=True) 'フォルダー内の不要ファイルの削除 Kill mypath & "*.bat" Kill mypath & "*.rar" 'フォルダー内のフォルダー数 folderPath = mypath '--- 含まれるフォルダ名を知りたいフォルダのパス ---' '--- ファイルシステムオブジェクト ---' Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") '--- フォルダ数を格納する変数 ---' Dim n As Long CountFolder = fso.GetFolder(folderPath).SubFolders.Count 'フォルダー内のフォルダーを書き出す Set MyF = fso.GetFolder(folderPath) ' 含まれるフォルダ名を知りたいフォルダを返します。 intR = 1 For Each SubF In MyF.SubFolders 'サブフォルダーを取得します。 Cells(intR, "A") = SubF.Name intR = intR + 1 Next Set fso = Nothing 'フォルダー名を元に戻す '指定位置から文字列抜き出し() With Sheets("DATA") EndRow = .Cells(Rows.Count, "A").End(xlUp).Row For I = 1 To EndRow Nubering3 (I) KokoKara = Application.InputBox(prompt:="何番目から抜き出すか? 数値を入力してください", Title:="指定位置(数値入力)", Type:=1) If TypeName(KokoKara) = "Boolean" Then MsgBox "数値以外が入力されたので終了します。" Exit Sub End If .Activate MojiSuu = Len(.Range("A" & I)) Nukidashi = Mid(.Range("A" & I), KokoKara, MojiSuu) .Range("B" & I) = Nukidashi Next I End With Sheets("Number").Range("A1:XX100").Clear 'FilenameChange() '指定したファイル名を変更します。 Set ws01 = Worksheets("DATA") FolderName = mypath 'ターゲットフォルダー lRow = ws01.Cells(Rows.Count, "A").End(xlUp).Row 'A列の最終行を取得 For I = 1 To lRow '最終行まで繰り返す OldFile = FolderName & "\" & ws01.Cells(I, "A") NewFile = FolderName & "\" & ws01.Cells(I, "B") MsgBox NewFile Name OldFile As NewFile 'ファイル名を変更します。 Next I End Sub Sub Nubering3(ByVal DataRow As Long) Dim Ws1 As Worksheet, Ws2 As Worksheet Dim I As Long, j As Long, WRow As Long, WColumn As Long Dim uRows As Range, uRange As Range Dim font1 As Font Set Ws1 = Sheets("DATA") Set Ws2 = Sheets("Number") Set uRows = Ws2.Rows(1) Set uRange = Ws2.Range("A2") 'Numberシートの初期化(全体=数式・文字・書式・コメント全てをクリア) Ws2.Range("A1:XX100").Clear Application.ScreenUpda
- kkkkkm
- ベストアンサー率66% (1725/2595)
Dir関数でサブフォルダを取得する http://officetanaka.net/excel/vba/tips/tips95.htm 上記の最後のコードだと GetAttr関数で見つけられないファイル名(フォルダ名)「#abcとか(abcあいうえお)」があり利用できなかったので VBA フォルダ名の取得または変更する https://www.tipsfound.com/vba/18023 こちらの最後を参考にして 以下の部分だけですが、変更してみてください。 'フォルダー内のフォルダーを書き出す Range(Cells(1, "A"), Cells(500, "A")).ClearContents Dim SubF As Object Dim MyF As Object Set MyF = fso.GetFolder(folderPath) ' 含まれるフォルダ名を知りたいフォルダを返します。 intR = 1 For Each SubF In MyF.SubFolders 'サブフォルダーを取得します。 Cells(intR, "A") = SubF.Name intR = intR + 1 Next Set fso = Nothing
お礼
No7.での 訂正のコードをありがとうございます。 修正コードで複数ファイルの書き出しがうまく処理できました。 ただ、ちょっと不具合があります。 下記のようにStopで一度停止しないと思ったように フォルダー名で抜き出しが上手くできないのです。 ChDir mypath CreateObject("Wscript.Shell").Run sPath 'Call obj.Run(sPath, WaitOnReturn:=True) Stop <-----挿入 !! 'フォルダー内の不要ファイルの削除 Kill mypath & "*.bat" Kill mypath & "*.rar" ------------------------- 私が勝手な想像ですが、 BAT処理が完全に終わるのを待つ必要があるように思えます。 以下の添付画像は、 正常に処理できた場合と結果がおかしい場合の画像です。 https://imgur.com/w9bb1wF
- kkkkkm
- ベストアンサー率66% (1725/2595)
「ファイルやフォルダの操作もVBAで」 はNo2で指摘されてましたね。 No4は完無視してください。
補足
以下に仮のコードを作成してみました。 (過去教えてもらったコードの組み合わせ) 「'フォルダー内のフォルダー数」までは、うまく処理されていますが 「'フォルダー内のフォルダーを書き出す」が最後の1個しか書き出されていません。 原因は何でしょうか ? --------------------------------------- Sub MooveUp_Directory() Dim strPath As String Dim intPathLen As Integer Dim intR As Integer Dim F As Variant Dim obj As WshShell Dim sPath As String Dim folderPath As String Dim CountFolder As Single Dim strFlName As String 'Range("A5:F100").Clear ' フォルダーを自由に選べること。 参考:officeTANAKA With Application.FileDialog(msoFileDialogFolderPicker) If .Show = True Then If Len(.SelectedItems(1)) = 3 Then ' c:\の場合とサブフォルダーの場合 mypath = .SelectedItems(1) Else mypath = .SelectedItems(1) & "\" End If End If End With If mypath = Empty Then MsgBox "フォルダー名の指定をキャンセルしました。": Exit Sub MyName = Dir(mypath, vbDirectory) ' 最初のフォルダ名を返します。 'Range("A2") = mypath 'コピー先フォルダーの指定 'BATファイルのコピー FileCopy "C:\MoveUp_Directory.bat", mypath & "MoveUp_Directory.bat" 'batファイルの起動 sPath = mypath & "MoveUp_Directory.bat" 'MsgBox mypath ChDir mypath CreateObject("Wscript.Shell").Run sPath 'Call obj.Run(sPath, WaitOnReturn:=True) 'フォルダー内の不要ファイルの削除 Kill mypath & "*.bat" Kill mypath & "*.rar" 'フォルダー内のフォルダー数 '--- 含まれるフォルダ名を知りたいフォルダのパス ---' folderPath = mypath '--- ファイルシステムオブジェクト ---' Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") '--- フォルダ数を格納する変数 ---' Dim n As Long CountFolder = fso.GetFolder(folderPath).SubFolders.Count MsgBox CountFolder 'フォルダー内のフォルダーを書き出す Range(Cells(1, "A"), Cells(500, "A")).ClearContents strFlName = Dir(mypath & "*", vbDirectory) MsgBox strFlName Do While strFlName <> "" If Replace(strFlName, ".", "") <> "" Then Cells(1, "A") = strFlName intR = intR + 1 End If strFlName = Dir Loop End Sub 'フォルダー名を元に戻す Sub 指定位置から文字列抜き出し() Dim MojiSuu As Single Dim KokoKara As Variant Dim i As Single Dim Nukidashi As String Dim EndRow As Single With Sheets("DATA") EndRow = .Cells(Rows.Count, "A").End(xlUp).Row For i = 1 To EndRow Nubering3 (i) KokoKara = Application.InputBox(prompt:="何番目から? 数値を入力してください", Title:="指定位置(数値入力)", Type:=1) If TypeName(KokoKara) = "Boolean" Then MsgBox "数値以外が入力されたので終了します。" Exit Sub End If .Activate MojiSuu = Len(.Range("A" & i)) Nukidashi = Mid(.Range("A" & i), KokoKara, MojiSuu) .Range("B" & i) = Nukidashi Next i End With Sheets("Number").Range("A1:XX100").Clear End Sub Sub Nubering3(ByVal DataRow As Long) Dim Ws1 As Worksheet, Ws2 As Worksheet Dim i As Long, j As Long, WRow As Long, WColumn As Long Dim uRows As Range, uRange As Range Dim font1 As Font Set Ws1 = Sheets("DATA") Set Ws2 = Sheets("Number") Set uRows = Ws2.Rows(1) Set uRange = Ws2.Range("A2") 'Numberシートの初期化(全体=数式・文字・書式・コメント全てをクリア) Ws2.Range("A1:XX100").Clear Application.ScreenUpdating = False WRow = 1: WColumn = 1 For j = 1 To Len(Ws1.Cells(DataRow, "A").Value) Ws2.Cells(WRow, WColumn).Value = j Ws2.Cells(WRow + 1, WColumn).Value = Mid(Ws1.Cells(DataRow, "A").Value, j, 1) Set uRange = Union(uRange, Ws2.Cells(WRow + 1, WColumn)) Set uRows = Union(uRows, Ws2.Rows(WRow)) If j Mod 40 = 0 Then WRow = WRow + 3 WColumn = 1 Else WColumn = WColumn + 1 End If Next 'Numeling 大文字、中央揃え uRows.HorizontalAlignment = xlCenter uRows.Font.Bold = True 'フォントサイズ指定 uRows.Font.Size = 9 '分割文字中央揃え罫線外枠 uRange.HorizontalAlignment = xlCenter uRange.Borders.LineStyle = xlContinuous 'フォントサイズ指定 'uRange.Font.Name = "HGP創英角ポップ体" uRange.Font.Size = 9 'セル幅を見やすく Ws2.Range("A1:xx100").ColumnWidth = 3 Application.ScreenUpdating = True Ws2.Activate Set Ws1 = Nothing Set Ws2 = Nothing Set uRows = Nothing Set uRange =
- kkkkkm
- ベストアンサー率66% (1725/2595)
ファイルやフォルダの操作もVBAでやってみてはいかがでしょう。 VBAでファイルの操作 http://officetanaka.net/excel/vba/tips/tips91.htm
- m5048172715
- ベストアンサー率16% (860/5261)
カレントディレクトリをc:\tempにして、手動でaa.batを起動すると、 aa.batは、コードの通り結果をC:\temp\内に書き出す。 ところがVBAからC:\temp\aa.batを起動すると、 aa.batは、コード通りの結果をC:\temp\内に書き出さない。 > WScript.Shell関数を使えってことらしい。 https://teratail.com/questions/234105 そうなのか。 では、 > 【VBA】別アプリケーションを起動【Wscript.ShellのRunを使う】 https://daitaideit.com/vba-wscript-shell-run/ を参考にC:\temp\aa.batを起動してみる。 ではどうかな?
補足
私が参考にした記事では、 WshShellのクラスを利用する とありました。 https://vbabeginner.net/execute-batch-file/ そうでは無く、回答は 「WScript.Shell関数を使えってことらしい。」 との事ですね。 記事を見て以下のように変更してみましたが 結果は、同じくC:\temp\内に書き出していません。 'batファイルの起動 sPath = mypath & "MoveUp_Directory.bat" MsgBox sPath Set obj = New WshShell CreateObject("Wscript.Shell").Run sPath 'Call obj.Run(sPath, WaitOnReturn:=True) ------------------ MsgBox sPathでは、 C:\temp\bb.bat とバッチは\temp内にちゃんと存在しています。
- asciiz
- ベストアンサー率70% (6812/9685)
VBAからファイルやフォルダの操作をしたいなら、FileSystemオブジェクト使った方がいいのでは…? >【エクセルVBA入門】フォルダやファイルを操作するFileSystemオブジェクトとその使い方 >https://tonari-it.com/excel-vba-filesystem/ >【VBAでフォルダ/ファイル操作】(3)すべての下位フォルダ内のファイルを操作する | PAC.Lab >https://tech-paclab.com/vbafsofiles03/ またバッチファイルというのは、エラー表示があまりなかったり、自分自身も削除できてしまったり、かなりデバッグしづらいものです。 また、知らないうちに aa.bat の内容が変わっていて、久しぶりに実行しようとしたら全く想定通りに動かない、なんてことも起きたりします。 ---- また、aa.bat を作業フォルダにコピーして、作業して、その後に削除、なんていうのもバグの元になりやすい手法です。 その様なことをしたい場合、 (1) "aa.bat C:\temp" のようにパラメータ付きで呼び出す (2) pushd $1 として、バッチ本体はコピーせず、作業フォルダに移動する (2) 指定フォルダでの作業 (3) 作業が終わったら popd といったように組むほうが良いでしょう。 ---- そもそもこのようなフォルダ操作をする意味というのは…? フォルダが占めるディスク領域を最小化したいとかでしょうか…?
補足
回答ありがとうございます。 VBA初心者の私が書いているコードは、 今回も含めて大半がネットからの情報や 以前からの利用しているコードの修正を組み合わせたモノです。 VBAのマスターのように悲しいかな思い道理には事が進みません。 アドバイスのいただいた点を理解するには時間が掛かりそうです。 >そもそもこのようなフォルダ操作をする意味というのは…? temp内のAAに同じフォルダー名のAAがあるので temp\AA\AAでは無く temp\AAと繰り上げた形式に変更したいのです。
- m5048172715
- ベストアンサー率16% (860/5261)
解答をズバリじゃないのだけど、 FileCopy "C:\aa.bat", mypath & "aa.bat" かな?質問者がおかしいというのは。 そこの、mypathを、 "c:\temp"または"c:\temp\"に置き換えて実験してみる。
補足
回答ありがとうございます。 >"c:\temp"または"c:\temp\"に置き換えて実験してみる。 置き換えて実験しなくても "C:\aa.bat", mypath & "aa.bat"のままで aa.batは、間違いなく"c:\temp\aa.batと指定のディレクトリーに コピーされています。 VBAを利用しないで 手動でc:\temp\でaa.batを起動すると 問題なく思っていた結果がC:\temp\内に書き出されます。
- 1
- 2
お礼
kkkkkさん、いつも回答をいただき感謝いたします。 >'batファイルの起動の前に >ChDir mypath >にしてみるとか まさに、ディレクトリーをTemp内に移動して batを実施すると思っていた処理が出来ました。 これから、他の処理を追加してみます。 一応、全ての処理が出来た時点でコードを明示したいと思います。 (明日には何とか形に出来るとおもいます。)