Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

フォルダーのバックアップを考える 2

今日はちょっと余所見をしているな。俺もちょっと余所見をしよーっと横向いている柴犬です。

概要

前回 PowerShell で行ったことが、拡張子「.bat」のバッチファイルで行おうとするとどうすればよいのか考えてみました。

VBAとはまた違った規則があり戸惑うところも多くありましたが何とかできるところまで到達できましたので記録します。

「PowerShell」「Windows .bat」とも少しかじっただけですが「PowerShell」のほうが細かいところまで細工が出来そうな感じがしました。

このような機能は、Googleドライブから一括で自分のPCにコピーすることもできますので、もっと工夫するといろいろ使えそうな気がします。

今回お世話になった本です。

コード

Googleドライブのファイルを一括で取得するためにコピー元を「G:」にしています。

また、「Windows .bat」には配列がありません。

コードの中で使っている flist[!num!] は単なる変数の名前です。

考えやすいので配列擬きの名前にしています。

ですから、変数の数を拾うために maxnum でカウントしています。

copy

@echo off
rem ----------遅延の展開を有効にします
setlocal enabledelayedexpansion

rem ----------「=」の前後にスペースを入れません
set cfolder=G:
set pfolder=C:\tmp1

rem ----------遅延展開をします
rem 足算をしますので /a オプションを指定します 
set /a num=0
set maxnum=0
rem ここはVBAにない構文です。('dir ・・・)はイテレータの働きをしています
rem なので、この例ではGoogleドライブあるものすべて(直下のファイルを除き)コピーします。
for /D  %%f in ('dir "%cfolder%\*" /b') do (
  call echo "%%f" | find "\" > nul
  if !errorlevel!==0 (
    rem %ではなく!を使います
    set flist[!num!]=%%f
    set maxnum=!num!
    set /a num=num+1
  )
)

rem ----------フォルダーの取得を確認します
for /l %%b in (0,1,%maxnum%) do (
  call echo %%flist[%%b]%%
)

rem ----------コピーを実行します
for /l %%b in (0,1,%maxnum%) do (
  rem /E ファイルが存在しなくてもディレクトリごとコピーします
  rem /I コピー先のディレクトリが存在しない場合は新規にディレクトリを作成します
  rem /S 空の場合を除いて,ディレクトリとサブディレクトリをコピーします
  rem /Y 同名のファイルが存在する場合、上書きの確認を行いません
  rem /Z ネットワーク経由のコピーでネットワーク切断が発生した場合、コピーを再開できるようにします
  call xcopy /E /I /S /Y /Z "%%flist[%%b]%%" "%pfolder%\"
)

endlocal

rem ----------メンテ用
rem pause

バッチの保存、実行

メモ帳でコードを書いた後のファイルの保存は次のようにします。

メモ帳の文字コードはデフォルトが「UFT-8」になっていますので、これでは全角の文字が正しく表示されません。「ANSI」にして保存します。

① メニュー「ファイル」をクリックして表示されるメニュー中の「名前を付けて保存」をクリックします。

② フォーム「名前を付けて保存」が開きます。

③ 「ファイルの種類」を「すべてのファイル」にします。

④ 「文字コード」を「ANSI」にします。

⑤「ファイル名」を適宜名づけ、拡張子は「.bat」にします。

⑥ 保存ボタンを押下します。

作成先をデスクトップにしたので、次のようなアイコンが作成されました。

実行は、アイコンをダブルクリックするだけです。

画像を取るため、最後の rem を外して実行してみました。

私のネット・PC環境では1秒足らずで実行されました。

ちなみに文字コード「UTF-8」で保存して実行すると次のような結果になりました。

マイクロソフトの公式HP

マイクロソフトの次の公式HPを読んでみます。

https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/setlocal

パラメータの説明をみてみると、何か意味不明の文章です。原文を読んでみるとマッチする endlocal まで有効ということを言いたいのだと分かります。

enabledelayedexpansionにより、一致するまで遅延環境変数の拡張 endlocal する前に設定に関係なく、コマンドが発生しました、 setlocal コマンドが実行されました。
enabledelayedexpansionEnables the delayed environment variable expansion until the matching endlocal command is encountered, regardless of the setting before the setlocal command was run.

そうであれば、次のコードにできるはずです。

実行してみたところ同じ結果が得られました。

copy

@echo off
rem ----------遅延の展開を有効にします
setlocal enabledelayedexpansion

rem ----------「=」の前後にスペースを入れません
set cfolder=G:
set pfolder=C:\tmp1

rem ----------遅延展開をします
rem 足算をしますので /a オプションを指定します 
set /a num=0
rem ここの行を削除しました
rem ここはVBAにない構文です。('dir ・・・)はイテレータの働きをしています
rem なので、この例ではGoogleドライブあるものすべて(直下のファイルを除き)コピーします。
for /D  %%f in ('dir "%cfolder%\*" /b') do (
  call echo "%%f" | find "\" > nul
  if !errorlevel!==0 (
    rem %ではなく!を使います
    set flist[!num!]=%%f
    rem ここの行を削除しました
    set /a num=num+1
  )
)

rem ----------フォルダーの取得を確認します

rem 追伸 ここでミスをしました(
rem !num!-1ではいけません これは文字列になってしまいます
rem 次のように計算してから、計算結果を使うようにしなければいけません
set /a maxind=!num!-1

rem %maxnum% → !num!-1 → %maxind% にしました
for /l %%b in (0,1,%maxind%) do (
  call echo %%flist[%%b]%%
)

rem ----------コピーを実行します
rem %maxnum% → !num!-1 → %maxind% にしました
for /l %%b in (0,1,%maxind%) do (
  rem /E ファイルが存在しなくてもディレクトリごとコピーします
  rem /I コピー先のディレクトリが存在しない場合は新規にディレクトリを作成します
  rem /S 空の場合を除いて,ディレクトリとサブディレクトリをコピーします
  rem /Y 同名のファイルが存在する場合、上書きの確認を行いません
  rem /Z ネットワーク経由のコピーでネットワーク切断が発生した場合、コピーを再開できるようにします
  call xcopy /E /I /S /Y /Z "%%flist[%%b]%%" "%pfolder%\"
)

endlocal

rem ----------メンテ用
rem pause

setlocal enabledelayedexpansion

for構文で変数の値を %変数名% を指定して使うと、for構文を評価する時に変数展開されるため、繰返し処理して加算した値を使いたくても値は最初のままで固定になります。

これを解決するために、EnableDelayedExpansion を指定して有効化した上で、「%」を「!」に置き換えて !変数名! にして使うと、for構文の繰返し処理ごとに変数を展開するため、算術(加算など)した値を使うことができます。

setlocal enabledelayedexpansion

set /a num=0

for /l %%i in (1,1,4) do (
  set /a num=num+1
)

echo %num%

endlocal
pause

上のコードを実行してみました。

4を期待しますが、残念ながら出力された値は 0 のままです。

「%」を「!」に変えて変数を囲み出力してみます。

setlocal enabledelayedexpansion

set /a num=0

for /l %%i in (1,1,4) do (
  set /a num=num+1
)

echo !num!

endlocal
pause

今度は期待通り 4 となりました。

今日はここまでとします。