それはバグでしょうか? 実行時に現在のコマンドウィンドウがすぐに閉じられます: move nul 2>&0

それはバグでしょうか? 実行時に現在のコマンドウィンドウがすぐに閉じられます: move nul 2>&0

結局、私はmove nul 2>&0私のスクリプトでは、現在の cmd ウィンドウを「突然」閉じて、実行中の bat を直ちに中止/閉じます。

脚本では自殺のように機能しているとは言えませんが、私は通常それを自殺と呼んでいます。

実際には、これにより、現在のコマンド ウィンドウにも効果が拡張/到達する自動強制終了が発生します。

それは単に走っているバットを閉じる/終わらせるということ以外の何かです。

基本的に、変数の値が"%cmdcmdline%"単にバットを閉じるのではなく、クリックによって呼び出された実行によって応答し、バットをクリックしてアクティブ化/ロードされたウィンドウも閉じます。

私が疑問に思うのは、

  1. この行動の理由は何でしょうか?
  2. それはバグですか?
  3. 将来、同じように動作しない可能性がありますか?

いくつかのコードサンプル:

@echo off 

title <nul 
title to kill or not to kill?

mode 50,05

echo\%cmdcmdline%|find "%~0" >nul && (
     echo\
     echo\ so sorry bro, I'll kill myself
     echo\ and this CMD.exe window too!
     
     timeout 5 /nobreak 
     move nul 2>&0 
   )

echo/ will run this code bro!
echo/
echo/ keep itself alive: %date% %time:~0,5%
echo/
pause

答え1

「現在のコマンドウィンドウ」という表現に困惑しています。何を意味しているのかわかりません。私の考えでは、あなたは2つの異なる概念を組み合わせているようです。

  • コンソールプロセスがあります。これはターミナルに似ていますが、まったく同じではありません。
  • そして、コンソールウィンドウ内で実行されているcmd.exeプロセスがあります

コマンドによって cmd.exe プロセスがクラッシュし、コンソール プロセスは内部で実行されているプロセスがなくなったため正常にシャットダウンします。

これは、追加の子 cmd.exe プロセス内でコマンドを実行すると簡単に証明できます。子 cmd.exe はクラッシュしますが、親は有効なままなので、コンソール ウィンドウも実行されたままになります。コンソールで実行されているまったく新しい cmd.exe プロセスから開始する例を次に示します。

Microsoft Windows [Version 10.0.18363.1256]
(c) 2019 Microsoft Corporation. All rights reserved.

P:\>set context=outer

P:\>cmd
Microsoft Windows [Version 10.0.18363.1256]
(c) 2019 Microsoft Corporation. All rights reserved.

P:\>set context=inner

P:\>move nul 2>&0

P:\>set context
context=outer

P:\>

最後に context = outer となっていることは、内部環境が失われたことを証明しています。その理由は、内部プロセスがクラッシュしたためです。その後、EXIT コマンドを発行すると、外部の cmd.exe プロセスが終了し、コンソールが閉じます。

次に、コマンドが cmd.exe をクラッシュさせる理由について説明します。MOVEコマンドやnulデバイスの使用に特別な点はありません。重要な点は、cmd.exe がエラー メッセージを stdin にリダイレクトした後、stderr に書き込もうとしていることです。明らかにこれは機能しないため、書き込みは失敗します。これが cmd.exe のクラッシュを引き起こすトリガーです。

ゼロ除算エラーでも、同じ効果が得られますSET /A 1/0 2>&0

リダイレクトが実際に成功することに注目することが重要です。リダイレクトは、stdin が出力を受け入れられないことを気にせず、盲目的にリダイレクトを実行します。失敗は、cmd.exe エラー書き込みルーチンがリダイレクトされた stderr に実際に書き込もうとしたときにのみ発生します。

stderr に書き込まないコマンドを単純にリダイレクトすると、リダイレクトが成功することが簡単に示されます。たとえば、echo Hello world 2>&0無意味なリダイレクトを実行して、メッセージを stdout に正常に書き込みます。

DosTips のどこかに、cmd.exe が I/O エラーを処理する方法を調査した投稿があります。その投稿を見つけたいのですが。テストでは、stdout または stderr をリムーバブル デバイスにリダイレクトしてから一時停止する操作を行ったと思います。一時停止中にデバイスを取り外してから続行します。次に、stdout または stderr 経由で失われたデバイスに書き込もうとしたときの動作を観察します。私の記憶では、stderr への書き込みが失敗すると、cmd.exe がすぐにクラッシュします。

これはバグか設計上の欠陥か? よく分かりません。エラー報告が失敗した場合の最も安全な方法は終了することだと主張する人もいるでしょう。しかし、stdout への書き込みが失敗しても、エラー メッセージやエラーがまったく返されないため、それが計画された「機能」だったとは思えません。出力は単に消え去り、cmd.exe は何も悪いことが起こらなかったかのように平気で続行します。stdout への書き込み時にバッチが失敗を検出することは不可能です。これはひどいと思います。cmd.exe の開発者は、その可能性をまったく考慮しなかったとしか思えません。したがって、それが本当であれば、stderr の失敗がクラッシュするという事実は、簡単に (幸運な?) 事故である可能性があります。

stdout と stderr の動作には他にもいくつか違いがありました。どちらもバッファリングされていますが、バッファ オーバーフローが発生した場合の動作が異なります。その違いが何だったか思い出せたらいいのですが :-(

アップデート

私の調査のいくつかはhttps://www.dostips.com/forum/viewtopic.php?t=6881

stdout への書き込みは、上記の説明で示唆されているよりも少し複雑です。Cmd.exe には、stdout への書き込みを試みる複数の内部ルーチンがあります。たとえば、次のようになります。

  • ECHO コマンド
  • SET /P メッセージ (入力プロンプト)
  • コマンドラインプロンプトとバッチコマンドラインのエコー(ECHOがオンの場合)
  • DIR などのコマンドの出力。

stdout に書き込むこれらのエントリ ポイントの一部は、書き込みが失敗した場合に独自の動作をする場合があります。上記のリンクでは、たとえば ECHO と SET /P の違いについて説明しています。

確信はありませんが、stderr へのエラー メッセージは異なると思います。すべてのエラー報告は、書き込みエラーが発生するとクラッシュする単一の内部ルーチンを通じて行われると思います。上記のリンクでは、この点についてはあまり調査されていません。I/O エラー (およびバッファリングの問題) をより詳しく調査した、ずっと後の投稿をまだ探しています。

関連情報