Это будет ошибкой? Немедленное закрытие текущего окна cmd при выполнении: move nul 2>&0

Это будет ошибкой? Немедленное закрытие текущего окна cmd при выполнении: move nul 2>&0

В конце концов, я используюmove nul 2>&0в моих скриптах, чтобы «внезапно» закрыть текущее окно cmd и, таким образом, немедленно прервать/закрыть запущенный bat.

Не могу сказать, что в сценарии это выглядит как самоубийство, но я обычно это так называю.

По сути, это приводит к автоматическому завершению работы, которое распространяется/достигает эффекта также в текущем окне cmd.

Это нечто большее, чем просто закрытие/окончание бегущей биты.

В основном используется для некоторых партий, где значение переменной"%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

Я борюсь с вашей фразой "текущее окно cmd" - не уверен, что вы имеете в виду. В моем представлении вы объединяете два разных понятия

  • Есть консольный процесс — аналогичный, но не совсем такой же, как терминал
  • И есть процесс 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 = external в конце, доказывает, что внутренняя среда была потеряна, причина в том, что внутренний процесс рухнул. Если я затем выполню команду EXIT, то внешний процесс cmd.exe завершится и консоль закроется.

MOVEТеперь о том, почему ваша команда приводит к сбою cmd.exe. В использовании вами команды или устройства нет ничего особенного nul. Критическая часть заключается в том, что cmd.exe пытается записать сообщение об ошибке в stderr после того, как оно было перенаправлено в stdin. Очевидно, что это не может работать, поэтому запись не удается — это триггер, который приводит к сбою cmd.exe.

Того же эффекта можно добиться с помощью ошибки деления на ноль SET /A 1/0 2>&0.

Важно отметить, что перенаправление фактически успешно. Redirect не заботится о том, что stdin не может принять вывод, он слепо выполняет перенаправление. Ошибка происходит только тогда, когда процедура записи ошибок cmd.exe пытается фактически записать в перенаправленный stderr.

Легко продемонстрировать, что перенаправление выполняется успешно, если просто перенаправить команду, которая не пишет в stderr. Например, echo Hello world 2>&0выполняет бессмысленное перенаправление и успешно записывает сообщение в stdout.

Где-то на DosTips у меня есть несколько постов, в которых я исследовал, как cmd.exe обрабатывает ошибки ввода-вывода. Хотел бы я найти этот пост. Я думаю, что мой тест включал перенаправление 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. Например:

  • Команда ЭХО
  • Сообщение SET /P (запрос на ввод)
  • Приглашение командной строки и эхо пакетной командной строки (когда ECHO включено)
  • Вывод команд типа DIR и т.п.

Некоторые из этих точек входа, которые пишут в stdout, могут иметь собственное поведение при сбое записи. Моя ссылка выше описывает различия между ECHO и SET /P, например.

Я не уверен, но я думаю, что сообщения об ошибках в stderr отличаются. Я думаю, что все сообщения об ошибках направляются через одну внутреннюю процедуру, которая падает при возникновении ошибки записи. Моя ссылка выше на самом деле не исследует это. Я все еще ищу гораздо более поздние сообщения, которые лучше исследовали ошибки ввода-вывода (и проблемы буферизации)

Связанный контент