
В конце концов, я используюmove nul 2>&0
в моих скриптах, чтобы «внезапно» закрыть текущее окно cmd и, таким образом, немедленно прервать/закрыть запущенный bat.
Не могу сказать, что в сценарии это выглядит как самоубийство, но я обычно это так называю.
По сути, это приводит к автоматическому завершению работы, которое распространяется/достигает эффекта также в текущем окне cmd.
Это нечто большее, чем просто закрытие/окончание бегущей биты.
В основном используется для некоторых партий, где значение переменной"%cmdcmdline%"
в ответ на выполнение вызванного щелчком мыши, вместо простого закрытия летучей мыши, также закрывая окно, которое было активировано/загружено щелчком мыши.
Мне интересно:
- Чем объясняется такое поведение?
- Это ошибка?
- Может ли быть, что в будущем он будет вести себя иначе?
Некоторые примеры кодов:
@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 отличаются. Я думаю, что все сообщения об ошибках направляются через одну внутреннюю процедуру, которая падает при возникновении ошибки записи. Моя ссылка выше на самом деле не исследует это. Я все еще ищу гораздо более поздние сообщения, которые лучше исследовали ошибки ввода-вывода (и проблемы буферизации)