@noeru02

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

batにおけるerrorlevelの挙動について

解決したいこと

Windwos上でとあるbatファイルを作成しています。
コマンド実行後、errorlevelを使用してエラー処理を実装していますが、
コマンドプロンプト上から当該batを呼び出す場合は、errorlevelは想定通り動作します。
(直前のコマンド結果がエラーの場合はerrorlevelに0以外の数値が入る)

しかし、エクスプローラ上からダブルクリック実行にて当該batを呼び出す場合、
errorlevelは0のままとなります。
何故上述の様な動作の違いが発生するのでしょうか。
また、ダブルクリック実行で想定通り動作させるにはどうすればよいでしょうか。

発生している問題・エラー

for %%s in (`dir C:\aaaaaaaaa`) do (
    echo "for文内のerrorlevel:%errorlevel%"
)

echo "for文後のerrorlevel:%errorlevel%"

dir C:\aaaaaaaaaは単に存在しないパスです。

自分で試したこと

プロンプト呼び出しの場合は、for文内、for文直後共にerrorlevelは1となります。
ダブルクリック実行した場合は、for文内、for文直後共にerrorlevelは0となります。
ダブルクリック時、管理者実行はしていませんので、実行パスはプロンプト呼び出しと同じ場所(batファイルが格納されている階層)で実行しています。
呼び出し方で結果が異なる理由が分からず、ご教示頂ければと思います。

0 likes

3Answer

ちょっとググっても明確な答えが出てこなかったので、以下はある程度自分の憶測が混ざっています。

とりあえず、コマンドプロンプトで「 if /? 」を実行してみてください。
ifコマンドの説明が出力され、その中に ERRORLEVEL の説明も含まれていると思います。
それによると、(ちょっとこれを書いているのはUbuntuなのでコピペできないんですが)「最後時実行したプログラムの終了コードが適用される」と書かれています。

この「プログラム」の定義とは一体何かはハッキリとわかりませんが、一般的には「新しくプロセスを作成して実行されるもの」と考えられます。

それに対して、指定されたバッチ内で実行されている「for」「dir」「echo」などは、全て「内部コマンド」と呼ばれるもので、新しくプロセスを作成せずに、実行中のシェルが解釈して実行されるものです。
つまり、上記の「プログラム」には当てはまらず、ERRORLEVEL に終了コードが反映されることはないのではないか、と思います。

そうなると、ERRORLEVEL は何が設定されているかというと、

  • コマンドプロンプトで実行した場合は、バッチファイルの前に実行したプログラムの終了コード
  • エクスプローラから実行した場合は、新しく CMD.EXE のプロセスが作成され、その上でバッチファイルが実行されるので、バッチファイルの前に実行したプログラムがないため、0

になるのではないでしょうか。
ちなみに、前者を試してみましたが、確かにバッチファイルの前に実行したプログラムの終了コードが出力されました。

2Like

Comments

  1. @noeru02

    Questioner

    ご教示頂きまして、有難うございます。
    if /?の実行結果について、確認させて頂きました。
    仰られる通り、「最後に実行したプログラムの終了コードが適用される」の「プログラム」の定義がキモになりそうです。
    推測頂いた内容については、非常に納得いきました。

自分の環境(Windows11 ビルド26100.4770)では、プロンプト呼び出し、ダブルクリック実行 、PSで実行の どれも同じで、errorlevelは 0 でした。
なんでしょうね?

1Like

Comments

  1. @noeru02

    Questioner

    ご教示頂き、有難うございます。
    今まである程度感覚でツール的なbatを作成していたので、errorlevelを意識して使用していなかったため、今回の質問に至りました。
    正しく仕様を理解する必要があると改めて感じております。。。

help forまたは公式のオンラインドキュメントをよく確認して下さい。


変数%%sの中身を見ればよく分かるのですが

for %%s in (`dir C:\aaaaaaaaa`)

はリスト文字列と見なされて結果的に1回目のループでdir、2回目のループでC:\aaaaaaaaaの文字列が変数%%sに返りませんか?

ヘルプには

usebackq - 次の新しい表示形式を指定します。
逆引用符で囲まれた文字列がコマンドとして実行され、一重引用符で囲まれた文字列がリテラル文字列コマンドになり、ファイル セットのファイル名を二重引用符で囲めるようになります。

つまり/Fスイッチに"usebackq"オプションが無いため、コマンドとして認識・実行されてないのです。

for /f "usebackq" %%s in (`dir "C:\aaaaaaaaa"`) do (
    @echo %%s
)

変数%%sdirコマンドの結果内容が返ります。

コンソール入力では以前のERRORLEVELを継承するので挙動がバッチファイルと異なるのは当然のことです。

そもそもforコマンドで何がしたいのか解らないためこれ以上回答はできかねます。

1Like

Comments

  1. @noeru02

    Questioner

    ご教示頂きまして、有難うございます。
    そもそもfor文の理解が不足していたようです、失礼致しました。
    まずは公式ドキュメントについて、確認をする様に致します。

Your answer might help someone💌