□技術メモ - DOSバッチ処理について ※管理人の個人的な技術メモです。このページの内容の実行結果について 管理人はいかなる責任も負いかねますのでご自身の責任でお試しください。 ----------------------------------------------------------- ○基本事項 @ この記号をコマンドの先頭に置くとコマンドが画面に表示されない。 | パイプ。左のコマンドの実行結果を右のコマンドに渡す。 rem 注釈。ここから右の文字列は実行されない。 ただし rem /? とするとremの機能説明が画面表示されるので注意。 : 文字列の先頭に:を置くことでラベルを指定する。 条件分岐をする際などに使用する。 % 環境変数の左右を%で挟むことで環境変数の値を展開する。 %abc% は、変数abcの値が展開される。 -------- ○exitの使用について ・exitは現在のコマンドインタープリタを終了する。 ・バッチ内において、現在のバッチを終了させたい場合は、exit /B とする。 ・/B がない場合は呼び出し元のcommand.com, cmd.exe も終了してしまうので注意すること。 -------- ○ifの構文 ・良く見かけるifの基本的構文は以下の通り。 if %x%==1 ( ... ) else if %x%==2 ( ... ) else ( ... ) -------- ○文字列の加工 ・文字列を加工する場合の構文は以下の通り。 %x:変換の書式% ・a文字目からb文字分を取得する場合は以下の通り。 aが負なら後ろからカウントする。 bが負なら後ろのb文字を除く。 %x:~a,b% ・文字列s1をs2に変換する。 %x:s1=s2% -------- ○日付・時刻 ・日付、時刻は %date% で取得する。 ・yyyy/MM/ddをセットする。(デフォルトの書式) set TODAY=%date% ・yyyyMMddをセットする。( / がなくなる) set TODAY=%date:/=% ・yyyyMMddをセットする。( 位置を指定して切り出した場合) set TODAY=%date:~0,4%%date:~5,2%%date:~8,2% ・時刻の半角スペースを0にセット。TIMENOW1の書式は hh:mm:ss.ff set TIMENOW1=%time: =0% ・TIMENOW2に hhmmssff をセットする。 set TIMENOW1=%time: =0% set TIMENOW2=%TIMENOW1:~0,2%%TIMENOW1:~3,2%%TIMENOW1:~6,2%%TIMENOW1:~9,2% -------- ○xcopyについて ・拡張コピーコマンド。tree構造ごとコピーするなどのオプションが多い。 ※注意! ・xcopyコマンドで、コピー元にフォルダを指定した場合の意味は、コピー元フォルダをコピーするという意味ではなく、 コピー元フォルダの下のすべてのフォルダとファイルをコピーするという意味である。 従って、以下の2つは同じ結果になる。(/S はすべてのファイルとサブフォルダをコピーするという意味) xcopy c:\tmp1 d:\tmp1\ /S xcopy c:\tmp1\* d:\tmp1\ /S ・従って、指定したフォルダをコピーする、という感覚で操作したい場合は、 コピー先に同じ名前のフォルダを作成しておけば良いということになる。 ・このことが分かっていれば、コピー先のフォルダ名が、コピー元と違っていても問題ない。 例: xcopy c:\tmp1 d:\tmp2\ /S この場合、c:\tmp1 の下のすべてのフォルダとファイルが、D:\tmp2 の下にコピーされる。 ・コピー元のフォルダに\を付けると何もコピーされずに終了する。原因は次のコマンドと同じ意味になるからと思われる。 xcopy c:\tmp1\(何も指定しない) c:\tmp2\ /S コピー元の最後には、\ を付けないように気を付けること。 ・下記の「xcopyの使い方 テスト」に動作結果を記してある。確認すること。 ・WindowsのDOSコマンドはバージョンによって微妙に動作が異なる。 意図した通りの動作になっているかよく確認してから実行すること。 ・オプションの例。tree構造ごとコピーする場合に必要。 /S すべてのサブフォルダとファイルをコピーする。空のフォルダはコピーしない。 /S /E すべてのサブフォルダとファイルをコピーする。空のフォルダを処理対象に含める。(E : Empty) /T フォルダのみコピーする。空のフォルダはコピーしない。 /T /E フォルダのみコピーする。/E はemptyの略。空のフォルダを処理対象に含める。 /Y 無条件に上書きコピーする。上書きの問い合わせをしない。 /C エラーを無視してコピーを継続する。(C : Continue) ・その他、状況に応じて必要なオプション。 /R 読取専用ファイルを処理に含める。 /H 隠しファイルを処理に含める。 /D 更新日付を比較してコピー元が新しい場合のみコピーする。古いファイルの上書きを回避する場合に設定する。 /D:MM-DD-YYYY 指定の年月日以降のファイルのみコピーする。 ・コピーから除外するファイルのリストを指定する場合は /EXCLUDE:ファイルパス を指定する。 例: xcopy c:\test d:\test\ /EXCLUDE:c:\bat\excludelist.txt /S /E /Y /C /R /H ( /D はファイルが古いバージョンに戻るのを防ぐ場合に設定する。無条件にコピーする場合は必要ないのでよく検討すること) 除外リストの例: $~ ~$ ~WRL Thumbs.db @done @work @backup -------- ○xcopyの使い方 例1 ・c:\temp 内のファイルをフォルダ構造ごと d:\temp にコピーする。 メッセージは%logfile%に追加書き込みされる。 set logfile=c:\log\logfile.txt xcopy c:\temp d:\temp\ /S /E /Y /C /R /H >> %logfile% -------- ○xcopyの使い方 例2 ・バックアップの例。丁寧に実行したい場合は、この様に行えばよいと思う。 ・c:\201606 配下に複数のフォルダがあり、フォルダ単位でコピーしたいとする。 その場合は先に xcopy /T /E でフォルダ構成を作り、フォルダ単位で xcopy を実行すればよい。 ・existは、ファイル、ドライブ、パス の存在確認を行う。 ・mkdirは、フォルダ作成、rmdirは、フォルダ削除を行う。 ・以下はバックアップの例。 if not exist d:\201606-backup\. ( mkdir d:\201606-backup ) if exist d:\201606-backup\. ( xcopy c:\201606 d:\201606-backup\ /T /E ) xcopy c:\201606\01 d:\201606-backup\01\ /S /E /Y /C /R /H /D xcopy c:\201606\02 d:\201606-backup\02\ /S /E /Y /C /R /H /D xcopy c:\201606\03 d:\201606-backup\03\ /S /E /Y /C /R /H /D xcopy c:\201606\04 d:\201606-backup\04\ /S /E /Y /C /R /H /D (以下同様) (/D はファイルが古いバージョンに戻るのを防ぐ場合に設定する) -------- ○xcopyの使い方 テスト ・ここでは例1の使い方についてテストを行う。 1.事前準備 以下のようなフォルダ構成を作成する。 c:\tmp1\a1\b2\c3\d4.txt c:\tmp2 \tmp1 の \a1 以下を \tmp2 の下にコピーすることを考える。 2.テスト1 コピー先に \a1 がない場合 以下のコマンドを実行してみる。 xcopy c:\tmp1\a1 c:\tmp2\ /S /E /Y /C /R /H /D ----(1) xcopy c:\tmp1\a1 c:\tmp2\a1 /S /E /Y /C /R /H /D ----(2) xcopy c:\tmp1\a1 c:\tmp2\a1\ /S /E /Y /C /R /H /D ----(3) 3.テスト2 コピー先に \a1 がある場合 以下のコマンドを実行してみる。 xcopy c:\tmp1\a1 c:\tmp2\ /S /E /Y /C /R /H /D ----(4) xcopy c:\tmp1\a1 c:\tmp2\a1 /S /E /Y /C /R /H /D ----(5) xcopy c:\tmp1\a1 c:\tmp2\a1\ /S /E /Y /C /R /H /D ----(6) 4.結果 (1) \b2 から下のみコピーされた。 ※注意! ・xcopy の意味は、指定のフォルダの下の全てのフォルダとファイルをコピーするという意味なので、 b2 以下がコピーされるのが正しい動作ということになる。 (2) 「C:\tmp2\a1 は受け側のファイル名ですか、またはディレクトリ名ですか?」 (2-1) ファイル名を選択した場合 「a1」という名前のテキストファイルが作成された。内容は d4.txt と同じ。 (2-2) ディレクトリ名を選択した場合 \a1 以下正しくコピーされた。 (3) \a1 以下正しくコピーされた。 (4) \b2 から下のみコピーされた。 (5) \a1 以下正しくコピーされた。 (6) \a1 以下正しくコピーされた。 ・どうやらコピー元のフォルダ名、コピー先のフォルダ名を一致させて、  両方のフォルダが存在する、という条件が確実なようです。 ・xcopy /S は、指定のフォルダをコピーするのではなく、指定のフォルダの下の全てのフォルダとファイルを コピーする、という意味であることに注意すること。 -------- ○xcopyの使い方 テスト2 ・上記のテスト1の(2)、以下のコマンドで、ファイルを選択した場合について、さらに動作確認を進める。 xcopy c:\tmp1\a1 c:\tmp2\a1 /S /E /Y /C /R /H /D ・ここでは a1 がファイルなのか、フォルダなのか分からないので、問い合わせが表示されたが、 コピー先を a1.txt と指定しても、フォルダの可能性があるとして、ファイル/フォルダの問い合わせがあることに注意。 ファイルであることを選択して進める。 ・c:\tmp2 は空とする。 ・以下のテキストファイルを追加する。 c:\tmp1\a1\b2\c4.txt c:\tmp1\a1\b2\c5.txt c:\tmp1\a1\b2\c3\d4.txt c:\tmp1\a1\b2\c3\d5.txt ・以下を実行した。 xcopy c:\tmp1\a1 c:\tmp2\a1.txt /S /E /C /R /H /D ・結果は、c4.txt と、c5.txt のコピーが行われて、 後から書き込まれた c5.txt の内容で、a1.txt が作成された。 ファイルコピーなので、b2 の直下にある c4.txt c5.txt のみが処理の対象となって、 d4.txt d5.txt は行われないことが分かった。 また、copy では、ファイルの連結が行われるが、xcopy では、ファイルの連結が行われないことが分かった。 -------- ○xcopyの使い方 テスト3 ・上記のテスト1の(3)、以下のコマンドを実行した場合について、さらに動作確認を進める。 xcopy c:\tmp1\a1 c:\tmp2\a1\ /S /E /Y /C /R /H /D ・テスト1の(6)では、コピー先に a1 がある状態で実施して、c:\tmp2\a1\a1 が作成されなかったのだから、 これは a1フォルダをコピーするコマンドではないことが分かる。 ・テスト1の(3)では、コピー先に a1 がない状態で実施して、c:\tmp2\a1 が作成された。 これは、a1 が作成されたのだろうか? それとも a1 がコピーされたのだろうか? ・コピーなのか、新規作成なのかの判定には、フォルダの更新日付で判定した。 コピーの場合、作成日付はコピー時刻になるが、更新日付はコピー元の日付と同じになる。 ・以下の3つのコマンドを実行した。いずれもc:\tmp2の下には何もない状態とする。 (7) xcopy c:\tmp1\a1 c:\tmp2\a1\ /S /E /Y /C /R /H (8) xcopy c:\tmp1\a1 c:\tmp2\z1\ /S /E /Y /C /R /H (9) xcopy c:\tmp1\a1 c:\tmp2\x1\y1\z1\ /S /E /Y /C /R /H ・実行結果は以下の通り。 (7) コピー先に a1 が作成され、更新日付はコピー元と同じだった。 つまり、a1フォルダのコピーが行われた。 (8) コピー先に z1 が作成され、更新日付はコピー元と同じだった。 つまり、a1フォルダの名前が z1に変更されてコピーが行われた。 (9) コピー先に x1, y1, z1 が作成された。 x1 は、新規作成された。 y1 は、新規作成された。 z1 は、更新日付がコピー元と同じ。つまり、 a1フォルダの名前が z1に変更されてコピーが行われた。 ・結論として、xcopy /S でフォルダをコピーする場合、 コピー先の一番下の階層のフォルダはコピー元からコピーされ、 間にある階層のフォルダが存在しない場合はそれを補完するようにフォルダが作成されることになる。 ・したがって、コピー先に a1 フォルダがあってもなくても、以下のコマンドでフォルダのコピーは可能、というのが結論になる。 (ただし本来の意味は、コピー先に a1フォルダがない場合はコピー元からコピーして、パスを作成して、 コピー元の a1 配下の全てのファイルとフォルダをコピーする) xcopy c:\tmp1\a1 c:\tmp2\a1\ /S /E /Y /C /R /H -------- ○copyについて ・コピーコマンド。ファイルコピーの際に使う。 コピー元のファイルの結合が可能。 ・オプションは以下の通り。 /a アスキー形式でコピー /a バイナリファイルとしてコピー /v ベリファイを行う。 /y 上書き確認を行う。 /-y 上書き確認を行わない。 /z ネットワーク経由のコピーでセットワークが切断した場合に、 処理を再開できるようにする。 ・特殊な入出力を設定できる。以下の通り。 con コンソール(画面出力、もしくはコンソール入力) nul ダミー。 copy nul ファイル名 とすることで空ファイルを作成できる。 windowsのバージョンによって動作が違うので詳細は要確認。 prn プリンタ ・ * 記号でワイルドカード指定ができる。 ・コピー元が複数、コピー先が1つの場合、ファイルが結合される。 + 記号で複数ファイルを指定することも可能。 ・環境変数 COPYCMD を設定できる。 COPY / MOVE / XCOPY において、/y もしくは /-y オプションをデフォルトに設定できる。 設定は以下の通り。 set COPYCMD=/y -------- ○copyの使い方 テスト1 ・オプションで設定可能は、/a /b はどのような働きをするのだろうか? また、ファイル結合が起きる際に、sjis と utf8 が混在している場合はどうなるのだろうか? ・同じフォルダに以下の2つのファイルが存在する時に、ファイル結合するとどうなるのか確認する。 なお、ファイルはいずれも sjis 形式とする。 ファイル名 内容 バイナリ a1.txt あ 82 A0 0D 0A b1.txt い 82 A2 0D 0A (1) copy a1.txt+b1.txt c1.txt (2) copy a1.txt+b1.txt c1.txt /a (3) copy a1.txt+b1.txt c1.txt /b ・実行結果 ファイル名 内容 バイナリ (1) c1.txt あ 82 A0 0D 0A い 82 A2 0D 0A 1A (2) c1.txt あ 82 A0 0D 0A い 82 A2 0D 0A 1A (3) c1.txt あ 82 A0 0D 0A い 82 A2 0D 0A ・アスキーモードでファイル結合すると、末尾にEOF記号の A1 が付加されます。 オプションなしで実行した場合はアスキーモードで実行されてやはり A1 が付きます。 詳細は記載しませんが、コピー元の拡張子をはずしたり、文字以外のコードをセットしても、 デフォルトだとやはりアスキーモードで実行されるようです。 最近だと EOF を付けることに特に意味は無いようなので、単純にバイナリで結合されていればよいときは /B を付ければ良いようです。(2017.12.22記) ただし、utf8 や unicode の場合は、先頭にBOMが付くことがありますし、 改行コードやファイル形式が混在の場合はどうなるのだろうかということが気になります。 実はこれは少々やっかいな問題のようで、アスキーモードの動きについて調べる必要があるようです。 全て調べるは時間が掛かりそうなので、以下の4つについて確認することにします。 ・unicode BOM付とBOM無の結合 ・unicode 改行コードが違う場合の結合 ・unicode と sjis の結合 ・utf8 と sjis の結合 -------- ○copyの使い方 テスト2 ・unicode BOM付とBOM無の結合について確認します。 ・文字コードとコピー元ファイルは以下の通り。 あ:42 30 い:44 30 う:46 30 ファイル名 内容 バイナリ a1.txt あ FF FE 42 30 0D 00 0A 00 //unicode、BOM付 b1.txt いう 44 30 46 30 0D 00 0A 00 //unicode、BOMなし (4) copy a1.txt+b1.txt c1.txt /a (5) copy b1.txt+a1.txt c2.txt /a ・実行結果 ファイル名 内容 バイナリ (4) c1.txt あ FF FE 42 30 0D 00 0A 00 いう 44 30 46 30 0D 00 0A 00 (5) c2.txt いう 44 30 46 30 0D 00 0A 00 あ 42 30 0D 00 0A 00 ・BOMなしを先に読み込んだら、結合後のファイルはBOMなしでした。 あくまでもファイルを順番に読み込んで結合するという処理をしているようです。 -------- ○copyの使い方 テスト3 ・改行コードが違う場合の結合について確認します。 ・コピー元ファイルは以下の通り。 ファイル名 内容 バイナリ a1.txt あ FF FE 42 30 0D 00 0A 00 //unicode、BOM付、CRLF b1.txt いう FF FE 44 30 46 30 0A 00 //unicode、BOM付、LF (6) copy a1.txt+b1.txt c1.txt /a (7) copy b1.txt+a1.txt c2.txt /a ・実行結果 ファイル名 内容 バイナリ (6) c1.txt あ FF FE 42 30 0D 00 0A 00 いう 44 30 46 30 0A 00 (7) c2.txt いう FF FE 44 30 46 30 0A 00 あ 42 30 0D 00 0A 00 ・改行コード混在の場合は特にどちらかに揃えるわけではなく、 そのまま結合しているようです。 -------- ○copyの使い方 テスト4 ・unicodeとsjisの結合について確認します。 ・コピー元ファイルは以下の通り。 ファイル名 内容 バイナリ a1.txt あ FF FE 42 30 0D 00 0A 00 //unicode、BOM付 b1.txt いう 82 A2 82 A4 0D 0A //sjis (8) copy a1.txt+b1.txt c1.txt /a (9) copy b1.txt+a1.txt c2.txt /a ・実行結果 ファイル名 内容 バイナリ (8) c1.txt あ FF FE 42 30 0D 00 0A 00 いう 82 A2 82 A4 0D 0A (9) c2.txt いう 82 A2 82 A4 0D 0A あ 42 30 0D 00 0A 00 ・文字コード混在の場合に、どちらかに文字コードを合わせるわけではないようです。 文字コードを識別する方法もないので当然そのまま結合されます。 文字化けの原因になるので注意が必要です。 後からBOMを読み込んだ場合は、BOMが外されるのは(4)(5)で確認した通りです。 -------- ○copyの使い方 テスト5 ・utf8とsjisの結合について確認します。 ・コピー元ファイルは以下の通り。 ファイル名 内容 バイナリ a1.txt あ EF BB BF E3 81 82 0D 0A //utf8、BOM付 b1.txt いう 82 A2 82 A4 0D 0A //sjis (10) copy a1.txt+b1.txt c1.txt /a (11) copy b1.txt+a1.txt c2.txt /a ・実行結果 ファイル名 内容 バイナリ (10) c1.txt あ EF BB BF E3 81 82 0D 0A いう 82 A2 82 A4 0D 0A 1A (11) c2.txt いう 82 A2 82 A4 0D 0A あ EF BB BF E3 81 82 0D 0A 1A ・UTF8のBOM「EF BB BF」は、外されずに残っています。 また、unicodeの場合は付いていなかったEOFが、utf8の場合は付いています。 ・アスキーモードとはいっても、改行コードの統一をするわけではなく、 BOMの処理や、EOFを付けるかどうか等、utf8 と unicode 等で処理が違うようです。 せっかくのアスキーモードなので、unicode のBigEndianとLittleEndianが統一されるかを確認しておきます。 -------- ○copyの使い方 テスト6 ・unicode のBigEndianとLittleEndianの結合について確認します。 ・コピー元ファイルは以下の通り。 ファイル名 内容 バイナリ a1.txt あ FF FE 42 30 0D 00 0A 00 //unicode, BOM付 b1.txt いう FE FF 30 44 30 46 00 0D 00 0A //unicodeBE, BOM付 (10) copy a1.txt+b1.txt c1.txt /a (11) copy b1.txt+a1.txt c2.txt /a ・実行結果 ファイル名 内容 バイナリ (10) c1.txt あ FF FE 42 30 0D 00 0A 00 いう FE FF 30 44 30 46 00 0D 00 0A (11) c2.txt いう FE FF 30 44 30 46 00 0D 00 0A あ 42 30 0D 00 0A 00 ・残念ながら、BigEndianを識別して文字コードを統一するということはしていないようです。 また、BOMについても「FF FE」が1番目のファイルになく、 2番目以降のファイルに「FF FE」が存在する場合はBOMを外す、という処理のようです。 ・デフォルトはアスキーモードで、通常のcopyの操作における処理速度に関わってくるので、 それほど丁寧に内部の解析をしている訳ではないようです。 ファイル結合でcopyコマンドを使う場合は文字コードやBOMをそろえるなど、注意した方が良さそうです。 --------