terminal: cmd: add environment variable to disable/enable 'Terminate batch job (Y/N)?' confirmation

(migrated from https://github.com/PowerShell/PowerShell/issues/7080 per @BrucePay’s suggestion)

When I cancel a cmd script with Ctrl C, the following dialog pops up:

Terminate batch job (Y/N)?

I understand why this exists, but I’d also like the option to disable it, so cancelling cancels immediately. Judging by Stack Overflow, I’m not the only one

So a request: could we have an environment variable to control this?

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 85
  • Comments: 65 (20 by maintainers)

Most upvoted comments

setlocal doesn’t behave the same way as an environment variable. It’s a thing that would have to be put in at the top of the batch script that is somefile.cmd as one of its first commands to adjust the way that one specific batch file is processed by the cmd.exe engine. That’s probably not suitable for your needs, but that’s the way we have to go.

I don’t think anyone is disagreeing with you, @mikemaccana, that this would be a five minute development change to read that environment variable and change the behavior of cmd.exe. It absolutely would be a tiny development time.

It’s just that from our experience, we know there’s going to be a 3-24 month bug tail here where we get massive investigation callbacks by some billion dollar enterprise customer who for whatever reason was already using the environment variable we pick for another purpose. Their script that they give their rank-and-file folks will tell them to press Ctrl+C at some point in the batch script to do whatever happens, it will do something different, those people will notice the script doesn’t match the computer anymore. They will then halt the production line and tell their supervisor. The supervisor tells some director. Their director comes screaming at their Microsoft enterprise support contract person that we’ve introduced a change to the OS that is costing them millions if not billions of dollars in shipments per month. Our directors at Microsoft then come bashing down our doors angry with us and make us fix it ASAP or revert it, we don’t get to go home at 5pm to our families or friends because we’re fixing it, we get stressed the heck out, we have to spin up servicing potentially for already shipped operating systems which is expensive and headache-causing…etc.

We can see this story coming a million miles away because it has happened before with other ‘tiny’ change we’ve been asked to make to cmd.exe in the past few years.

I would just ask you to understand that cmd.exe is very, very much in a maintenance mode and I just want to set expectations here. We maintain it, yes. We have a renewed interest in command-line development, yes. But our focuses are revolving around improving the terminal and platform itself and bringing modern, supported shells to be the best they can be on Windows. Paul will put this on the backlog of things that people want in cmd.exe, yes. But it will sink to the bottom of the backlog because changing cmd.exe is our worst nightmare as its compatibility story is among the heaviest of any piece of the operating system.

I would highly recommend that Gulp convert to using PowerShell scripts and that if such an issue exists with PowerShell, that we get their modern, supported, and better-engineered platform to support the scenario. I don’t want you to sit around waiting for cmd.exe to change this because it’s really not going to happen faster than that script could be converted to ps1 and it fixed in PowerShell Core (if that’s even a problem in that world.)

oh god I’m having ptsd flashbacks just reading that

@arcanis , I’ve realized that a known quirk of CMD can be used as a work-around.

GOTO undefined label is a fatal error. It immediately terminates batch processing, but the currently executing block of code that has already been parsed continues to completion. However, the remainder of the code is executed using a command line context instead of a batch context.

I’ve used this to work around another annoying CMD bug (the fact that exit /b 1 within a script doesn’t set the process exit code unless the script in question is called via call SCRIPT, which is obviously uncontrollable by the script itself; further explanation (and fix) is within the commentary for commit 9a7db966 that I added to pl2bat.

But you can use the same quirk here to kill the calling script, avoiding any “Terminate batch job (Y/N)?” prompt originating from the calling script, while still executing the remainder of the code in the parsed unit.

@rem ...
goto #_undefined_# 2>NUL || title %COMSPEC% & "%PROG_NAME%" %ARGS%
@rem ...

The title %COMSPEC% command is included in order to work-around the fact that using this prevents the caller from resetting the window title to a prior value which can lead to ever-expanding window titles. I think it’s a minor inconvenience for the benefits gained: both the suppression of “Terminate…” and correct process exit values, no matter how the script is called.

In example, I’m using the following code as a replacement for the npm shim for my installation of hexo

@setLocal
@echo off
goto :_START_

:set_real_dp0
@rem:: ref: "https://stackoverflow.com/questions/19781569/cmd-failure-of-d0-when-call-quotes-the-name-of-the-batch-file"
@rem:: ref: "https://stackoverflow.com/questions/12141482/what-is-the-reason-for-batch-file-path-referenced-with-dp0-sometimes-changes-o/26851883#26851883"
@rem:: ref: "https://www.dostips.com/forum/viewtopic.php?f=3&t=5057"
set dp0=%~dp0
set "dp0=%dp0:~0,-1%" &@rem:: clip trailing path separator
goto :EOF

:_START_
call :set_real_dp0

IF EXIST "%dp0%\node.exe" (
  SET "_prog=%dp0%\node.exe"
) ELSE (
  SET "_prog=node"
  SET PATHEXT=%PATHEXT:;.JS;=;%
)

endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\node_modules\hexo-cli\bin\hexo" %*

This construction of the hexo shim runs the node.exe executable in “command line mode” so any interrupts are not passed back up to the caller batch file. I’ve suggested that npm start using it as a template for future shims (see https://github.com/npm/cli/issues/969 and https://github.com/npm/cmd-shim/pull/46).

And, although I might personally consider this something of a “hack”, it uses the standard, non-patched, CMD shell. So, it should be fair game. And, I expect, some billion-dollar customer will soon be using it somewhere (if they aren’t already), so it should be stable. 😄

@All - @miniksa @zadjii-msft and @paulcam206 are not kidding - the unexpected impact of even the smallest change to Cmd has resulted in calamities, misfortune, lost-weekends and evenings, stress and unnecessary turmoil.

Cmd is in maintenance mode. It isn’t being deprecated or removed - in fact, I doubt it’ll EVER be removed from Windows, but do understand that Cmd is extremely fragile and difficult to change.

I keep saying it, and now that PowerShell is available almost everywhere - even Linux & macOS - it’s more true than ever:

If you’re writing Windows Command-Line scripts today, they should be PowerShell scripts wherever possible

@bitcrazed But, executing PowerShell script is not enabled by default.

I understand why you are terrified of changing anything in cmd.exe.

But:

  1. Define a brand new registry value (or even a whole new key) which nobody is using–if you are truly paranoid about the chance someone already has it set, put it somewhere unrelated to the registry keys cmd.exe already looks at, even put a random GUID in the key or value name so the chance that it already exists is astronomically low
  2. At cmd.exe startup, check if that registry value exists and has the value 1, and if so set an internal boolean flag
  3. If the boolean flag is set, skip displaying the Terminate batch job (Y/N)? prompt, act as if Y was answered. This is likely just one extra if statement (if that)

That way, people who want to make this prompt go away, can create the new registry key/value.

It is rather difficult to conceive of a way in which the above change could possibly break anything. If the registry key/value isn’t set, it behaves identically to at present. If setting it breaks something, the solution is to remove that registry setting.

It seems to me that cmd scripts are generally used in the place of exe symlinks and shebang scripts, which PowerShell is not a good replacement for - if you switch out yarn.cmd for yarn.ps1, that’s fine if you’re already in PowerShell, but if you’re in cmd, or some other shell, or using ShellExecute or some scripting language’s equivalent, you have to write pwsh -c yarn, so then why not get rid of the script altogether and do node yarn.js?

For this type of thing, we’re pretty much stuck with something in PATHEXT, and I think cmd is the only console interpreter in there.

@zadjii-msft Please correct me if I’m wrong (seriously! I hope I am 😄), but I think Powershell isn’t a good general option. Even if we add .ps1 in the PATHEXT, it seems that the default file association opens it with Notepad or VS, which prevents it from being a replacement for cmd being used as a kind of “enhanced symbolic link” (we pass additional arguments).

It’s your project, and any change would be your responsibility so I understand if it doesn’t happen, but the setlocal option sounded interesting (it requires scripts to opt-in, but precisely it requires scripts to opt-in), and would definitely be useful to us.

Then there’s just another billion-dollar enterprise out there that for some unknowable reason is already setting that flag in the registry for some other purpose, and will run into the same issues.

No enterprise has any business adding custom values to CMD’s configuration key, “[HKLM|HKCU]\Software\Microsoft\Command Processor”. Surely you have to draw some reasonable lines in the sand with your customers.

it’s possible that this could be done with something like a new setlocal setting. however, we’ve not added one of these in a long time. that said, I’ll file an item on our backlog to investigate 😃

With these words, you explain exactly why this change is so insidiously dangerous.

Can it get any worse than data loss, caused by the existing behavior, when it simply continues if you hit Ctrl+C twice? Someone deleted the whole database! 🥲

As is, it’s not just annoying, it’s dangerous enough already.

I don’t disagree, but somebody, somewhere, is depending on this behaviour.

You’re heard, for sure. But we’re not planning on touching cmd.exe. Literally last week we had a P0 fire where we broke the core Windows build because we tried making what seemed like a trivial internal change to the CMD.exe code. It’s literally burned us 100% of the times we’ve tried to touch it.

If you want a modern, updated shell on Windows, you should try out PowerShell 7. Or clink. Or yori.

Then there’s just another billion-dollar enterprise out there that for some unknowable reason is already setting that flag in the registry for some other purpose, and will run into the same issues.

I’m just going to leave this here 😄 https://github.com/sgraham/cmdEx for people that don’t accept reality and want to substitute it with something else.

Hello All ,

For anyone having issues ctr+c and break of batch. Then just use Keyfreez software. you can run keyfreez at the start of the batch and end it using taskkill command at the end of batch

It disable whole keyboard.

This looks to me like a case of the emacs spacebar. Obviously nobody wants to potentially deal with these kind of issues. On the other hand, this is a stupid feature that everyone wants to turn off globally. I agree that an environment variable is risky, but the registry key strikes me as the all-around best solution to this.

  • Cmd has been changed - it turns on ENABLE_VIRTUAL_TERMINAL_PROCESSING. Maybe other things as well, but I at least know of this one.
  • This change would be extremely small in scope and would make at least me disproportionately happy.
  • A setlocal would at least be an option, but every script would have to be updated so it doesn’t seem like a great way to do this.
  • The registry is already used for tons of potential breaking change backcompat type switches.
  • This “feature” is so obnoxious that if there were a way to bribe you guys I’d chip in a couple dollars for the cause.

I understand the fear of changing cmd and I can’t be upset with you guys if you don’t change this (although I can certainly be upset at whoever introduced it). If it did happen though, you guys are already minor personal heroes, but I guess it’d be an extra point there, and I think that’s all I got.

@miniksa @musm @zadjii-msft

.PS1’s problem:

  • PowerShell is still 10× times slower than CMD.
  • Scripts are disabled by default!

From my view, the best solution for “.cmd stubs” might be generating an EXE stub (like Shimmy), if it does not irrate anti-malware softwares. A code signature may reduce such false positives (sign the code part and put the command line pattern into the rest places.)

For npm, an interesting solution may be using .js files directly and launch them with WSH – and then forward it to the real .js entry points. UPDATE: It doesn’t work. WSH starts the Node process in a new window.

Something replacing .cmd and .bat? PS1 is too complex and too slow.


发件人: Jack Works notifications@github.com 发送时间: Tuesday, April 2, 2019 10:06:41 PM 收件人: Microsoft/console 抄送: Renzhi Li; Comment 主题: Re: [Microsoft/console] cmd: add environment variable to disable/enable ‘Terminate batch job (Y/N)?’ confirmation (#217)

cmd.exe is very, very much in a maintenance mode and I just want to set expectations here. We maintain it, yes. We have a renewed interest in command-line development, yes. But our focuses are revolving around improving the terminal and platform itself and bringing modern, supported shells to be the best they can be on Windows.

We’re not going to make changes to cmd.exe to support this scenario.

Okay, I understand this. But, is there an unofficial / hack solution to this? I searched the web and only found someone patching binary to do this. Unfortunately, that only works for Windows XP to and Windows 7

Then I found another project, https://github.com/sgraham/cmdExhttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fsgraham%2FcmdEx&data=02|01|Renzhi.Li%40microsoft.com|ea8b6c7e4ee14e5d780d08d6b7746eb7|72f988bf86f141af91ab2d7cd011db47|1|0|636898108031211694&sdata=Id%2FYm1klHq6nHjQY50Uph9FdTWHsGsTXRYOODmYox8s%3D&reserved=0 Only works for Windows 8.1 32bit

😦

― You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FMicrosoft%2Fconsole%2Fissues%2F217%23issuecomment-479012154&data=02|01|Renzhi.Li%40microsoft.com|ea8b6c7e4ee14e5d780d08d6b7746eb7|72f988bf86f141af91ab2d7cd011db47|1|0|636898108031211694&sdata=dVp4NGya3W6yH7NMFgg87lb38fhjj7r%2F%2Fmr3xTTwHuM%3D&reserved=0, or mute the threadhttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAAOp25uHVKve6DUbDGCsqYMg3lRvJlcDks5vc2PxgaJpZM4VGR0n&data=02|01|Renzhi.Li%40microsoft.com|ea8b6c7e4ee14e5d780d08d6b7746eb7|72f988bf86f141af91ab2d7cd011db47|1|0|636898108031221686&sdata=jVKSOX8R0tjLrlcqejdOgDPJ4pTVFg9FhuzSKS7fPAc%3D&reserved=0.

Keep speaking into void, folks. Whoever hopes this abomination will ever gets a proper fix, check this historic question: https://superuser.com/questions/35698/how-to-supress-terminate-batch-job-y-n-confirmation

It appears like nobody wants to approach cmd.exe and touch it with a stick, because “we can break something”. So cmd.exe has been in “maintenance mode”, while its legitimate use keeps growing due to the popularity of CLI tools like Node.js, NPM, Deno etc., which heavily rely upon .CMD and .BAT files on Windows.

I struggle to come up with even a contrived scenario where the proper Ctrl+C handling (especially behind a feature flag) could possibly break any legacy scripts. OTOH, I can think of a magnitude of cases where the current behavior causes annoyances and harm.

E.g., when the following batch file is executing and I hit Ctrl+C trying to stop it, the infamous "Terminate batch job (Y/N)?" prompt is shown. I hit Ctrl+C again to escape, but the execution is continuing instead, deleting my GIF files:

@echo off
echo About to delete *.gif. Press Ctrl+C to stop
pause
del /q *.gif

For a more convincing example, speak to this person:

image

Luckily, there’s new CreatePseudoConsole Win32 API that should make possible to create a proper shim for cmd.exe, to look for and intercept the "Terminate batch job (Y/N)?" prompt. Such shim can then be set as an active command shell via COMSPEC env variable.

When time allows, I’m going to explore this options, like I did to address another controversial behavior, paste-with-formatting-by-default.

This specific issue came in through Feedback Hub as well and I spent some time reproducing it. When I use conhost.exe, the first time I run the contrived script and do Ctrl+C twice, it exits properly. But subsequent runs, it deletes at the second one anyway. And when through Terminal… it just immediately goes to the second state and deletes.

So you’ve hit my interest further and I will be pulling this particular case out into a separate bug to figure out why the state changes and differs.

EDIT: MSFT:36733663

Then there’s just another billion-dollar enterprise out there that for some unknowable reason is already setting that flag in the registry for some other purpose, and will run into the same issues.

Surely you’re kidding.

This would prevent any changes to any aspect of Windows.

@parkovski This wouldn’t be safe at all since Windows has no equivalent of the execute bit (chmod +x).

Filesystems that support ACLs (i.e. those with the flag FILE_PERSISTENT_ACLS), such as NTFS (required for the system drive), have equivalent FILE_EXECUTE discretionary access control. CreateProcessW will fail with access denied (5) if the caller doesn’t have execute access.

For example, grant the owner of “spam.exe” generic execute access and the right to modify the discretionary ACL: icacls "spam.exe" /grant *OW:(GE,WDAC). (The File object type maps GENERIC_EXECUTE access to FILE_EXECUTE | FILE_READ_ATTRIBUTES | READ_CONTROL | SYNCHRONIZE. “OW” is SDDL for the “OWNER RIGHTS” security principle.)

We can also set SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP mandatory access restriction for caller’s at a lower integrity level than that of the file, but Windows doesn’t include a command-line utility to modify the no-execute-up flag in a file’s mandatory label.

One major problem is portable filesystems such as FAT32 and exFAT that lack security. You’d need a policy to disable shebang support for these filesystems. The next problem is that the default ACLs in Windows liberally grant execute access based on rules inherited from the root directory and the primary system directories (e.g. “Windows”, “Program Files”, “ProgramData”, “Users”). Removing inherited execute access would require assigning custom security instead of relying completely on inheritance.

Filetypes that ShellExecuteExW passes directly to CreateProcessW use "%1" %* as their command template, where "%1" is the file itself. This is currently used for “.EXE”, “.COM”, “.CMD” and “.BAT” files. In principle this can be extended to filenames that have no extension. To the shell this is “.”, because “spam” and “spam.” are equivalent in DOS.

Personally, I would like to see this feature tied somehow to developer mode developer mode could unlock more options not just side loading apps. If the big corporation is running servers in developer mode then well… (You could couple dev mode with the above suggestions)

I run npm start hit CTRL C, and I get the Terminate Batch Job Y/N I just HIT CTRL C why are you asking me this If I hit N it does the same as Y anyways in this example.

We’ve got enough on our hands with the new open-source Terminal. Thanks for the suggestion, though!

You could just open source cmd and somebody would just produce a highly compatible (but not fully), and with these tiny new things we all want. A cmd2, let’s say, or even a cmd we could replace the original one with. That big enterprise will never see it, and we all get to live a little bit better.

Why not? .exe is the execute bit. Totally open to corrections, but a shebang exe would be basically equivalent to a shim exe that just launches another process, without the overhead of actually loading a launcher process. All other security features that already apply to exes would still apply to this type; it’s just a special form where the shebang line replaces the exe name, just as .bat automatically invokes cmd.exe, or a shim exe can “fork out” to another exe.

As in, what is the difference between these two:

  • foo.exe: #!node ....
  • foo.exe: Invokes node with the text stored somewhere in the file that can be extracted with WinAPIs.

I don’t see any difference, except that one is easily human creatable/editable, while the other requires a compiler.

Look at the scoop package manager’s shims. They are a hack around this - an exe that loads a text file and launches it. I don’t see how extending the exe could possibly be any more dangerous than what is already available.

CMD.exe is not getting updated. I don’t know how to stress that any further.

@Jack-Works, we are not in the business of developing or propagating unofficial hacks to our software. If you figure something out, more power to you. But we can’t help you there.

@be5invis, I’m sure the Powershell team would be happy to accept your feedback and improve their product. They are the Microsoft team that has funding for the interactive shell space.

Honestly, changing cmd.exe at all is a bit terrifying. You can’t be sure who in the world is actually depending on that behavior for some reason or another.

relevant xkcd

@skissane

  1. Define a brand new registry value

Bad idea. It will affect all cmd.exe instances. Better, for example, to extend setlocal with a new flag like: setlocal DISABLECTRLBREAKINTERACTION

You’re heard, for sure. But we’re not planning on touching cmd.exe. Literally last week we had a P0 fire where we broke the core Windows build because we tried making what seemed like a trivial internal change to the CMD.exe code. It’s literally burned us 100% of the times we’ve tried to touch it.

If you want a modern, updated shell on Windows, you should try out PowerShell 7. Or clink. Or yori.

This would make a super interesting blog post (which you could point people to any time they ask this again) – maybe ping Raymond Chen? 😄

@zadjii-msft can you hear the people chanting “Terminate batch job (Yes/No) has got to go!”?

One option is to break cmd.exe, forcing all shims to be authored anew. Because the prompt is kind of on the journey there already.

It’s just that from our experience, we know there’s going to be a 3-24 month bug tail here where…

Yeah. But like those people will keep using Windows, as much as you break it for them, but the people in this thread will keep switching to macOS. Kind of apples to oranges. It’s been 5 years.

When I use conhost.exe, the first time I run the contrived script and do Ctrl+C twice, it exits properly. But subsequent runs, it deletes at the second one anyway. And when through Terminal… it just immediately goes to the second state and deletes.

Canceling the batch termination prompt uses no as the default answer, and thus the script continues to run. In particular, cmd!PromptUser() handles a failed/canceled or empty (EOF) read by defaulting the answer to no. That’s reasonable behavior in the general case (e.g. do not delete all the files in the “foo” directory for del foo). For the batch termination prompt, it probably would have been more reasonable to handle repeated Ctrl+C presses by terminating the script instead of canceling the prompt itself. (I’m thinking of a user frantically pressing Ctrl+C.) A parameter could have been added to PromptUser() to set the default answer instead of hard coding no as the default.

I was 85% joking, and 15% “we’ve been burned literally every time we’ve touched cmd.exe so it’s almost certainly not happening”

Even the ENABLE_VIRTUAL_TERMINAL_PROCESSING required us to go back and change it such that cmd.exe only enables that flag for itself, and disables it again for child processes.

billion dollar enterprise customer who for whatever reason was already using the environment variable we pick for another purpose

@miniksa Is it possible to introduce a flag in the registry instead of an environment variable for that purpose? As far as I can see, that would address this concern.

Yeah, I’m not too optimistic about this actually being embedded in Windows, but if we were allowed to have !# exes (and only .exes), I think that would solve a lot of these weird cmd script issues.

Oh, I see – you’re not advocating allowing any file to be a shebang script. I can’t speak to the can of worms that would open, nor do I ever think this would be considered but it’s interesting to ponder nonetheless.

I agree with the problem with using powershell here instead of cmd; which I agree would be ideal, except it’s far too slow in its initialization time. Even more unfortunately the new pwsh is even slower to initialize. This is something I opened an issue about, but the situation is not likely to be improved https://github.com/PowerShell/PowerShell/issues/6443.

Startup latency: 466 ms POWERSHELL 42 ms CMD

500 ms is way too long for a script, hence I’m not sure the solution to use ps1 is actually reasonable.

Here’s the script to test start up latency

> function time($block) {
    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $sw.Elapsed
}

> time {powershell -NoProfile -c exit}
> time {cmd-NoProfile -c exit}

@parkovski An interesting idea would be using WSF/WSH files.

It seems to me that cmd scripts are generally used in the place of exe symlinks and shebang scripts, which PowerShell is not a good replacement for

It’s true. For example, perl wraps all scripts installed from cpan with a few lines of batch code loading perl executable using its pl2bat script.

setlocal variant would be very helpful for perl because it would allow us to fix Terminate batch job (Y/N)? issue by making pl2bat add setlocal line to scripts generated with this tool .

Well, relevant to this thread, adding the setlocal option is just as good and addresses my concerns also - then all the projects that use cmd scripts this way just add another line at the top.

It still requires projects to opt into this behavior, and it’s not compatible with older versions of Windows (well, it’s compatible, just ignored), but it seems like the best way to do this to me. Doesn’t break any other cmd scripts assuming yarn is a command, and I’d be happy replacing my hacked together exe launcher with these.

The point is you can type yarn from the command line (which in Windows is powershell)

With all the recent console improvements and WSL, I think there’s a possibility of seeing alternate shells on Windows in the future. Cmd may be in maintenance mode, but people still use it too, so I’m not a huge fan of this assumption.

Personally I’d really like to see the setlocal option, regardless of how projects choose to handle this.

Thanks @bitcrazed. I appreciate the testimony directly from the horse’s mouth - I’ve submitted a PR to one of the many node projects that use cmd scripts, with a .ps1 replacement. Hopefully it gets accepted and we start moving node onto a more supported scripting language.

Ack @zadjii-msft - not proposing to change the default, so should only affect people who’ve explicitly set some very long string.

@paulcam206 thanks! I’m not sure about how setlocal works - my real experience of cmd is launching cmd files from powershell). Would I still be able to do this in Powershell:

$env:TERMINATE_IMMEDIATELY='yes'
somefile.cmd

Then press Ctrl C when I need to and have it work?