none
どうしてもバッチファイルが意図した動作をおこなってくれず、自力で解決できないので質問します。エラー処理に詳しい方お願いします。 RRS feed

  • 質問

  • 毎度お騒がせいたしております。

    今回は私の中ではミステリアスなことばかりなので、是非ともお手を拝借させてください。

    さて問題のコードは下記のとおりです。

    問題個所のコメント行の先頭には*が付けてあります。

    その他のコメントはエンドユーザー向けです。

    @echo off

    set lgd=%~d0%~p0error_%~n0.log
    echo ログファイルは%lgd%に保存されます。
    if exist "%lgd%" del %lgd%
    rem システムパーティションが含まれているフォルダをフルパスで指定します。
    set bksysfld=a:^\bcd^\efg^\hij^\
    rem dパーティションのバックアップが含まれているフォルダをフルパスで指定します。
    set bkdatfld=b:^\cde^\fgh^\ijk^\
    rem *まだerrflgは定義されていないので日時の出力があって問題ない
    if not exist "%bksysfld%" (
    echo %date% %time%
    echo システムパーティションのためのフォルダが見つかりませんでした。
    set errflg=1
    )
    rem *フォルダが存在しないのでこのエラーが表示されるのは想定どおり
    if not exist "%bkdatfld%" (
    rem *既にerrflgが定義されているので、日時は表示させたくない
    if not defined "%errflg%" (
    echo %date% %time%
    set errflg=1
    )
    echo 指定したデータパーティション用のバックアップ保存フォルダは存在しません。
    )

    rem 上で指定したフォルダから最新のファイルを特定しています。
    for /f %%i in ('dir /b /a-d /o-n %bksysfld%*.wim') do (
    rem *ファイルが見つからないのにエラーを表示できないのはなぜか
    if "%%i:~-4"==".wim" (
    if not defined %errflg1 (
    echo %date% %time%
    set errflg=1
    )
    else echo 指定されたシステムバックアップ保存用フォルダには該当するファイルが見つかりませんでした。
    )
    else set bsfn=%bksysfld%%%i
    for /f %%i in ('dir /b /a-d /o-n %bkdatfld%*.wim') do (
    rem *上のfor文と同様
    if "%%i:~-4"==".wim" (
    if not defined %errflg% (
    echo %date% %time%
    set errflg=1
    )
    else echo 指定されたデータバックアップ保存用フォルダには該当するファイルが見つかりませんでした。
    )
    else set bdfn=%bkdatfld%%%i
    rem *なぜここでもエラーを表示できないのか
    if defined %errflg% (
    echo 作業途中にエラーが生じたため、処理を中断し、Windowsに戻ります。
    wpeutil reboot
    )
    rem *もちろんファイルが存在しないが、diskpartがスクリプトを読めなかった旨のメッセージを表示できないのか
    else (
    diskpart /s %~d0%~p0crtprt.txt
    rem それぞれのパーティションに適応を行います。
    dism apply-image /imagefile:%bsfn% /index:1 /applydir:w:\
    dism apply-image /imagefile:%bdfn% /index:1 /applydir:d:\
    rem システムパーティションにブートコードエントリーを書き込んでいます。
    w:\windows\system32\bcdboot.exe /l ja-jp /s w:\windows s:
    wpeutil reboot
    )

    簡単に質問内容をまとめると、errflgは定義されていないことになっているのか、for____in_____do (ifをどうすれば実現できるかです。

    変数の宣言や型、条件の定義などに問題がありそうというところまでは理解しているつもりなのですが、もし違うところに問題があるなら、御面倒をおかけしますが、その点もご指摘くださるととてもうれしく思います。

    是非ともよろしくお願いします。

    2017年11月12日 22:51

回答

  • チャブーンです。

    一応コメントしておきます。

    遅延環境変数の話は、for文中でbdfnをsetしたあとに
    dismがbdfn指定しているところで使ってますから
    本筋の処理が動かないと思うんですがどうですか。

    大丈夫だと思います。元ネタのソースはかなりわかりづらいですが、56/57行目の話しかなと認識していますが、ここで使う%bdfn%は、46行目で定義されていったんIF構文の外に出ています。48行目のIF構文は新しいブロックなので、%bdfn%で表示される認識です。なので、先のような言い方になっています。

    あとlogファイルに何も保存されないと思う(というかファイル生成すらされない)んですが、
    そこはいいんですか?

    3行目に変数が指定されていても、その引数を使っていないので、おっしゃる通りではありますね。私のコメントの趣旨は、一番質問者が問題にしていそうな部分にフォーカスしたもので、コードを隅から隅までれびゅーしたものではありません。このログがあってもなくても質問の本意とは離れていると判断して、とくに言及しませんでした。

    私の個人的認識を述べただけで、批判の意図はありません。だからというわけでもないですが、「水も漏らさぬ完璧な回答」をこの文脈でしようとは、全く考えていません。


    フォーラムは有償サポートとは異なる「コミュニティ」です。フォーラムでご質問頂くにあたっての注意点 をご一読のうえ、お楽しみください。

    2017年11月16日 8:35
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017
    構文エラーですか…。
    構文ミスですが、構文エラーではありません。構文ミスを構文通り解釈してます。

    それならバッチ自身を止めて、
    バッチはエラーがあっても止まりません。

    文やfor文の外でもerrflgの値を出力したり挑戦もしてみたのですが、%errflg=1が許されないのは最後まで疑問が残ります。
    まだ理解できてないようですが、環境変数の置換は、複文全体を読み取って最初に1回行われます。したがって、複文の途中で変更しても後の祭りです。バッチの基本中の基本ですから、よく理解しましょう。

    if not "%errflg%"==1 (
    これも構文ミスですが、構文エラーにはならず、構文通り解釈されます。
    "%errflg%"==1
    常に偽と評価されます。だって、
    "1"==1
    は真になりません。



    2017年11月18日 11:52

すべての返信

  • oooohです。

    errflgをテキストで出力してif exist errflg.txtにすればよいのでは。

    for文の遅延環境変数はアレなので中の変数で処理しようとしない方が無難だと思います。

    2017年11月13日 4:13
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017

    if not defined "%errflg%"
    はおかしいでしょう。
       IF DEFINED 変数 コマンド
    ですよ。
    IF [NOT] 文字列1==文字列2 コマンド
    とは違いますよ。


    2017年11月13日 9:37
  • チャブーンです。

    この件ですが、直接原因は IF DEFINED構文の文法誤りであり、遅延環境変数は関係ないように思います。

    IF DEFINED構文は「IF DEFINED <変数名>」であり、このケースでは「IF not DEFINED errflg (」になります。ですから、

    if not defined %errflg% (

    は構文上間違いです。(notが含まれることは問題ありません) 変数名として認識しない文字列を入れていたのですから、条件に引っかからないのは当然です。

    で、このコードには「for文で定義したブロック内でerrflgを表示する」項目がないので、遅延環境変数は関係ないのですが、このような項目が必要な場合、以下のように設定します。サンプルなので、質問者さんのバッチとは直接関係させていませんが。

    setlocal  ENABLEDELAYEDEXPANSION
    
    if not defined errflg (
     echo %date% %time%
     set errflg=1
     echo エラーフラグ !errflg! が設定されました。
     )
    最後に余計なコメントかもしれませんが、この問題は単にコードの構文違いが原因で、「エラー処理かどうか」は完全に無関係です。スレッドタイトルに無関係な情報を付加した場合、真意が伝わりにくく、読み手が誤解する可能性が高いので、再考いただいた方がよろしいかと思います。

    フォーラムは有償サポートとは異なる「コミュニティ」です。フォーラムでご質問頂くにあたっての注意点 をご一読のうえ、お楽しみください。

    2017年11月14日 6:49
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017
    エラー処理に詳しい方お願いします。
    という話ではありませんね。シンタクス理解不足です。

    シンタクスの理解不足と言えば、
    set bksysfld=a:^\bcd^\efg^\hij^\
    ^によるエスケープは不要。
    引用符が必要な特別な文字は次のとおりです:
         <スペース>
         &()[]{}^=;!'+,`~
    次に
    if "%%i:~-4"==".wim" (
    これは何。for変数に部分文字列はないでしょう。環境変数と勘違いしてる。
    2017年11月14日 9:38
  • oooohです。

    えーと、definedの件は前の私のレスの通りで、

    if existにすれば関係ないので問題にしませんでした。

    遅延環境変数の話は、for文中でbdfnをsetしたあとに

    dismがbdfn指定しているところで使ってますから

    本筋の処理が動かないと思うんですがどうですか。

    あとlogファイルに何も保存されないと思う(というかファイル生成すらされない)んですが、

    そこはいいんですか?

    2017年11月16日 2:36
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017

    errflgをテキストで出力してif exist errflg.txtにすればよいのでは。

    for文の遅延環境変数はアレなので中の変数で処理しようとしない方が無難だと思います。

    そんなことをすれば、ファイルの後始末や初期化(あれば削除)など複雑余計なことが増えるだけ。変数のほうが無難で適切です。

    dism apply-image /imagefile:%bsfn% /index:1 /applydir:w:\
    は確かに遅延展開が必要ですね。
    call dism apply-image /imagefile:%%bsfn%% /index:1 /applydir:w:\
    %がエスケープされているので、複文読み取り時には展開されない。callで読み取り時に展開される。

    ん? 複文が切れてるみたいですね。なら要らない。
    2017年11月16日 3:42
  • oooohです。

    これはWinPE上のイメージ展開処理だと思いますので初期化・後処理は不要と考えます。

    2017年11月16日 4:07
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017

    そう言えば、
    if exist "%lgd%" del %lgd%
    ファイルは初期化してるのに、環境変数は初期化してませんね。いいのかな。
    set errflg=
    普通の環境で繰り返しテストが必要でしょうから、初期化は必要でしょう。
    2017年11月16日 4:51
  • 七辻家です。

    意図を汲み取れているか微妙ですが

    一応、WindowsPEで動かしてイメージの適用は出来ましたので

    試してみて下さい。

    並び順は「dir /o:n」のファイル名のアルファベット順でやってあります。

    ファイル名に日付が含まれている事が前提ですので

    日時順で行いたい場合は「dir /o:d」に変更して下さい。

    @echo off
    
    set lgd=%~dp0error_%~n0.log
    echo ログファイルは%lgd%に保存されます。
    
    if      exist "%lgd%" del /f "%lgd%"
    
    rem システムバックアップ保存フォルダをフルパスで指定します。
    set bksysfld=a:\bcd\efg\hij\
    
    rem データバックアップ保存フォルダをフルパスで指定します。
    set bkdatfld=b:\cde\fgh\ijk\
    
    rem エラーフラグを初期化します。
    set errflg=
    
    rem システムバックアップ保存フォルダの有無をチェックします。
    if  not exist "%bksysfld%" (
        if  not defined errflg echo;%date% %time%
        echo;システムバックアップ保存フォルダが見つかりませんでした。
        set /a errflg+=1
    ) >> "%lgd%"
    
    rem データバックアップ保存フォルダの有無をチェックします。
    if  not exist "%bkdatfld%" (
        if  not defined errflg echo;%date% %time%
        echo;データバックアップ保存フォルダが見つかりませんでした。
        set /a errflg+=2
    ) >> "%lgd%"
    
    rem システムバックアップ保存フォルダから最新のファイルを特定しています。
                                                                 set bsfn=
    for /f "delims=" %%i in ('dir /b /o:n "%bksysfld%*.wim"') do set bsfn=%%i
    if  not defined bsfn (
        if  not defined errflg echo;%date% %time%
        set /a errflg+=4
        echo;システムバックアップ保存用フォルダに該当するファイルが見つかりませんでした。
    ) >> "%lgd%"
    
    rem データバックアップ保存フォルダから最新のファイルを特定しています。
                                                                 set bdfn=
    for /f "delims=" %%i in ('dir /b /o:n "%bkdatfld%*.wim"') do set bdfn=%%i
    if  not defined bdfn (
        if  not defined errflg echo;%date% %time%
        set /a errflg+=8
        echo;データバックアップ保存用フォルダに該当するファイルが見つかりませんでした。
    ) >> "%lgd%"
    
    rem パーティション構成ファイルの有無をチェックします。
    if  not exist "%~dp0crtprt.txt" (
        if  not defined errflg echo;%date% %time%
        echo;パーティション構成ファイルが見つかりませんでした。
        set /a errflg+=16
    ) >> "%lgd%"
    
    rem エラーが無かったら、パーティション作成・イメージ適用・ブート書込します。
    if      defined errflg (
        echo;作業途中にエラーが生じたため、処理を中断し、Windowsに戻ります。
      ) >> "%lgd%" else (
        echo;パーティション構造を作成します。
        diskpart /s "%~dp0crtprt.txt"
        echo;それぞれのパーティションにイメージの適用を行います。
        dism /apply-image /imagefile:"%bksysfld%%bsfn%" /index:1 /applydir:w:\
        dism /apply-image /imagefile:"%bkdatfld%%bdfn%" /index:1 /applydir:d:\
        echo;システムパーティションにブートコードエントリーを書き込んでいます。
        bcdboot w:\windows /l ja-jp /s s:
    )   >> "%lgd%"
    
    wpeutil reboot
    

    2017年11月16日 8:35
  • チャブーンです。

    一応コメントしておきます。

    遅延環境変数の話は、for文中でbdfnをsetしたあとに
    dismがbdfn指定しているところで使ってますから
    本筋の処理が動かないと思うんですがどうですか。

    大丈夫だと思います。元ネタのソースはかなりわかりづらいですが、56/57行目の話しかなと認識していますが、ここで使う%bdfn%は、46行目で定義されていったんIF構文の外に出ています。48行目のIF構文は新しいブロックなので、%bdfn%で表示される認識です。なので、先のような言い方になっています。

    あとlogファイルに何も保存されないと思う(というかファイル生成すらされない)んですが、
    そこはいいんですか?

    3行目に変数が指定されていても、その引数を使っていないので、おっしゃる通りではありますね。私のコメントの趣旨は、一番質問者が問題にしていそうな部分にフォーカスしたもので、コードを隅から隅までれびゅーしたものではありません。このログがあってもなくても質問の本意とは離れていると判断して、とくに言及しませんでした。

    私の個人的認識を述べただけで、批判の意図はありません。だからというわけでもないですが、「水も漏らさぬ完璧な回答」をこの文脈でしようとは、全く考えていません。


    フォーラムは有償サポートとは異なる「コミュニティ」です。フォーラムでご質問頂くにあたっての注意点 をご一読のうえ、お楽しみください。

    2017年11月16日 8:35
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017
    for /f "delims=" %%i in ('dir /b /o:n "%bkdatfld%*.wim"') do set bdfn=%%i
    フルパスに拘らない、ということですね。
    ファイルをアルファベットの逆順に並べ、ディレクトリ名は覗いてフルパスで1件だけ取得するバッチファイルはどのように書けばいいですか?
    /S          指定されたディレクトリおよびそのサブディレクトリのすべての
                ファイルを表示します。
    ですから、フルパスを出すために/sを使うのは邪道でしょう。
    2017年11月16日 12:36
  • 丁寧な回答をありがとうございます。

    構文エラーですか…。それならバッチ自身を止めて、構文に誤りがあります'defined'の使用方法が間違っています?…ぐらいのメッセージが欲しいところです。

    運営者様是非適切なタイトルに書き換えていただけますように。。。

    今後はタイトル指定にも一層の考慮に努めたいと思います。

    if

    文やfor文の外でもerrflgの値を出力したり挑戦もしてみたのですが、%errflg=1が許されないのは最後まで疑問が残ります。

    これはちょっと愚痴なので回答は不要です。

    私は最終的に問題の部分はエラー出力に何度も使うのでサブルーチンとして

    :chkerrflg

    if not "%errflg%"==1 (

    echo %date %time%

    set errflg=1

    )

    exit /b

    のようにして行数も減らして入れ子も少なめにして仕上げました。

    皆さんいろいろな立場からのご意見ご指導感謝します。

    2017年11月17日 17:03
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017
    構文エラーですか…。
    構文ミスですが、構文エラーではありません。構文ミスを構文通り解釈してます。

    それならバッチ自身を止めて、
    バッチはエラーがあっても止まりません。

    文やfor文の外でもerrflgの値を出力したり挑戦もしてみたのですが、%errflg=1が許されないのは最後まで疑問が残ります。
    まだ理解できてないようですが、環境変数の置換は、複文全体を読み取って最初に1回行われます。したがって、複文の途中で変更しても後の祭りです。バッチの基本中の基本ですから、よく理解しましょう。

    if not "%errflg%"==1 (
    これも構文ミスですが、構文エラーにはならず、構文通り解釈されます。
    "%errflg%"==1
    常に偽と評価されます。だって、
    "1"==1
    は真になりません。



    2017年11月18日 11:52
  • 質問の意図がつかみきれませんが、[ドライブ文字]:[ディレクトリ][ファイル名][拡張子]を全て含んだ物をフルパスというと理解しています。

    dir /?

    とするとオプションが表示されます。

    その中にdir [drive letter]:\[folder name1]\[folder name2]\...\[file name(use * or ?)].[extention] /s /b /n-o

    でそのファイル名のリストが表示されるとあります。

    また/s /bはバッチファイルなどで扱うときに便利だとも記されています。

    for /f %%i in ('dir /s /b /o-n d:\*.*() do (

    set fullpath=%%i

    exit /b

    )

    echo %fullpath%

    を必要な部分に利用するかればいいと思います。

    これでは意図した結果は得られませんか?

    2017年11月18日 13:13
  • oooohです。

    >46行目で定義されていったんIF構文の外に出ています。

     48行目のIF構文は新しいブロックなので、%bdfn%で表示される認識です。

    同じバッチないですから!bdfn!じゃないと表示されませんよ。

    ちゃんと動作確認しましたか?

    >普通の環境で繰り返しテストが必要でしょうから、初期化は必要でしょう。

    普通の環境でDiskpart?ドライブレター的にもそれはありえない。

    追記↑↑のですが、%%でも!!でも引けました。(そんな挙動でしたっけ?)

    失礼いたしました。

    • 編集済み ooooh 2017年11月21日 5:36 追記
    2017年11月21日 4:31
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017

    ホントの構造は、七辻家さんのとおりで、複文は切れてる。forの)が抜けてる?

    このバッチはまだまだで、普通の環境で繰り返しテストしないとまともに動くようにならないでしょう。テスト時に実行しない文はremかechoにするのは常識テクでしょう。
    2017年11月21日 9:07
  • © ウィンドウズスクリプトプログラマ - Windows Script Programmer 2017

    質問者は視覚障害者ですよね。引用ブロックや参照リンクは認識できるのでしょうか?認識しにくいものがあれば、質問時に書いておくと、返信する側が配慮するでしょう。
    2017年11月21日 9:14
  • お世話になります。

    解答をもらいやすい投稿についての親切なアドバイス感謝します。

    どのように何を配慮してほしいか、きちんと伝えることの大切さを再認識させてくれる返信でした。

    今後ともよろしくお願いします。

    2017年11月26日 15:38