Can history expansion (exclamation mark) be disabled on Windows shell? - cmd

When I run a command like this:
someprogram.exe --param="Value!"
the Windows shell will treat the exclamation mark in my value as a special character. Is there a way to suppress that or is escaping the only way around this? (Exclamation mark is escaped by ^^! if I'm not mistaken.)
EDIT: Weird thing that cmd.exe does not do the expansion but cmder does. I thought they are just two different GUIs to the same underlying shell? Maybe there's a switch for the expansion behavior? I tried SETLOCAL DisableDelayedExpansion but with no effect.

Related

Windows echo command: The odds of cmd.exe escaping

I'm in the unfortunate position to be forced to invoke a program via echo <input> | program.exe. Of course, I wondered how to escape <input> and found:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Escape angle brackets in a Windows command prompt
In essence, it seems sufficient to escape all special chars with ^. Out of curiosity I still would like to know, why echo ingores double-quote escaping in the first place:
C:\>echo "foo"
"foo"
C:\>
Is there any normative reference?
Bonus question: How to echo the strings on and off with the echo command?
Edit: I just found this. I states that only |<> need to be escaped. However, expansion like %FOO% still work.
Special characters like ^, &, (, ), <, >, %, ! and " may cause problems with echo, also when trying to echo a string into a pipe; odd numbers of " are particularly difficult to handle.
Building escape sequences can be very complicated particularly with pipes, because such initiates new cmd instances for either side, so multi-escaping might become necessary.
The only reliable way to pipe the output of echo into a program is to use a variable holding the string to return and to apply delayed expansion, but within the left side of the pipe, like this:
cmd /V /C echo(^^!VARIABLE^^!| program.exe
Note the double-escaping of ! like ^^!, which makes this code even work when delayed expansion is also enabled in the parent cmd instance. There must not be a SPACE in front of the |, because this was echoed too otherwise. Note that echo terminates the output by a line-break.

What is "delayed environment variable expansion"

Is there someone to explain exactly what is delayed environment variable expansion in simple terms or point to a post that can understand. Here is my questions
What can't I achieve without this
In which practical situations uses this
Any alternative
%Var% is expanded when a line is read. As lines can have multiple commands, !var! is expanded when used. In MS-DOS !var! (accessed in script by %!var!%) is a legal variable name so you have to turn on a special mode to access. This is so MS-DOS batch files can run in CMD without editing.

bat files, functions and caret symbols

Whats's going on?
helper.bat
#echo off
echo %1
call:foo %1
goto:eof
:foo
echo %1
goto:eof
Run our script like the following
helper "^^^^"
Output
"^^^^"
"^^^^^^^^"
Why? I know that '^' symbol is smth special in case of cmd.exe, but what's going on here? How the function call affect on it?
CALL is very special in this case!
The batch parser has different phases, in the special character phase unquoted carets are used to escape the next character, the caret itself is removed.
In your case, the carets are quoted, so they will not be affected.
Then the carets can be affected again in the delayed expansion phase, but quotes havn't special meaning there, the carets are used only to escape exclamation marks.
Normally after the delayed phase all is done, BUT if you use CALL all carets are doubled.
Normally this is invisible, as the CALL also restarts the parser and carets are removed in the special character phase again.
But in your case they are quoted, therefore they stay doubled.
Try this
call call call call echo a^^ "b^"
Output
a^ "b^^^^^^^^^^^^^^^^"
The parser is explained at How does the Windows Command Interpreter (CMD.EXE) parse scripts?

Dealing with quotes in Windows batch scripts

In a Windows batch file, when you do the following:
set myvar="c:\my music & videos"
the variable myvar is stored with the quotes included. Honestly I find that very stupid. The quotes are just to tell where the string begins and ends, not to be stored as part of the value itself.
How can I prevent this from happening?
Thanks.
set "myvar=c:\my music & videos"
Notice the quotes start before myvar. It's actually that simple.
Side note: myvar can't be echoed afterwards unless it's wrapped in quotes because & will be read as a command separator, but it'll still work as a path.
http://ss64.com/nt/set.html under "Variable names can include Spaces"
This is the correct way to do it:
set "myvar=c:\my music & videos"
The quotes will not be included in the variable value.
It depends on how you want to use the variable. If you just want to use the value of the variable without the quotes you can use either delayed expansion and string substitution, or the for command:
#echo OFF
SETLOCAL enabledelayedexpansion
set myvar="C:\my music & videos"
As andynormancx states, the quotes are needed since the string contains the &. Or you can escape it with the ^, but I think the quotes are a little cleaner.
If you use delayed expansion with string substitution, you get the value of the variable without the quotes:
#echo !myvar:"=!
>>> C:\my music & videos
You can also use the for command:
for /f "tokens=* delims=" %%P in (%myvar%) do (
#echo %%P
)
>>> C:\my music & videos
However, if you want to use the variable in a command, you must use the quoted value or enclose the value of the variable in quotes:
Using string substitution and delayed expansion to use value of the variable without quotes, but use the variable in a command:
#echo OFF
SETLOCAL enabledelayedexpansion
set myvar="C:\my music & videos"
md %myvar%
#echo !myvar:"=! created.
Using the for command to use the value of the variable without quotes, but you'll have to surround the variable with quotes when using it in commands:
#echo OFF
set myvar="C:\my music & videos"
for /f "tokens=* delims=" %%P in (%myvar%) do (
md "%%P"
#echo %%P created.
)
Long story short, there's really no clean way to use a path or filename that contains embedded spaces and/or &s in a batch file.
Use jscript.
Many moons ago (i.e. about 8 years give or take) I was working on a large C++/VB6 project, and I had various bits of Batch Script to do parts of the build.
Then someone pointed me at the Joel Test, I was particularly enamoured of point 2, and set about bringing all my little build scripts into one single build script . . .
and it nearly broke my heart, getting all those little scripts working together, on different machines, with slightly different setups, ye Gods it was dreadful - particularly setting variables and parameter passing. It was really brittle, the slightest thing would break it and require 30 minutes of tweaking to get going again.
Eventually - I can be stubborn me - I chucked the whole lot in and in about a day re-wrote it all in JavaScript, running it from the command prompt with CScript.
I haven't looked back. Although these days it's MSBuild and Cruise Control, if I need to do something even slightly involved with a batch script, I use jscript.
The Windows command interpreter allows you to use the quotes around the entire set command (valid in every version of windows NT from NT 4.0 to Windows 2012 R2)
Your script should just be written as follows:
#echo OFF
set "myvar=C:\my music & videos"
Then you may put quotes around the variables as needed.
Working with the CMD prompt can seem esoteric at times, but the command interpreter actually behaves pretty solidly in obeying it's internal logic, you just need to re-think things.
In fact, the set command does not require you to use quotes at all, but both the way you are doing your variable assignment and the way the ,method of using no quotes can cause you to have extra spaces around your variable which are hard to notice when debugging your script.
e.g. Both of the below are technically Valid, but you can have trailing spaces, so it's not a good practice:
set myvar=some text
set myvar="some text"
e.g. Both of the below are good methods for setting variables in Windows Command interpreter, however the double quote method is superior:
set "myvar=Some text"
(set myvar=Some value)
Both of these leave nothing to interpretation the variable will have exactly the data you are looking for.
strong text However, for your purposes, only the quoted method will work validly because you are using a reserved character
Thus, you would use:
set "myvar=c:\my music & videos"
However, even though the variable IS correctly set to this string, when you ECHO the sting the command interpreter will interpret the ampersand as the keyword to indicate another statement follows.
SO if you want to echo the string from the variable the CMD interpreter still needs to be told it's a text string, or if you do not want the quotes to show you have to do one of the following:
echo the variable WITH Quotes:
Echo."%myvar%"
echo the variable WITHOUT Quotes:
Echo.%myvar:&=^&%
<nul SET /P="%myvar%"
In the above two scenarios you can echo the string with no quotes just fine. Example output below:
C:\Admin> Echo.%myvar:&=^&%
C:\my music & videos
C:\Admin> <nul SET /P="%myvar%"
C:\my music & videos
C:\Admin>
Try using the escape character '^', e.g.
set myvar=c:\my music ^& videos
You'll have you be careful when you expand myvar because the shell might not treat the & as a literal. If the above doesn't work, try inserting a caret into the string too:
set myvar=c:\my music ^^^& videos
Two solutions:
Don't use spaces or other characters that are special to the command interpreter in path names (directory or file names). If you use only letters, numbers, underscores, and hyphens (and a period before the extension to identify the file type), your scripting life will become immeasurably simpler.
I have written and otherwise collected a plethora of tools over the years, including a DOS utility that will rename files. (It began as something that just removed spaces from filenames, but morphed into something that will replace characters or strings within the filenames, even recursively.) If anyone's interested, I will get my long-neglected web site up and running and post this and others.
That said, variables aren't just for holding pathnames, so...
As others have already pointed out, SET "myvar=c:\my music & videos" is the correct workaround for such a variable value. (Yes, I said workaround. I agree that your initial inclination to just quote the value ("my music & videos") is far more intuitive, but it is what it is, as they say.

What's the cmd/PowerShell equivalent of back tick on Bash?

Redirecting command output:
For example:
echo "Foo `./print_5_As.rb`"
would echo "Foo AAAAA"
The PowerShell syntax is based on the POSIX ksh syntax
(and interestingly not on any of Microsoft's languages
like CMD.EXE, VBScript or Visual Basic for Applications),
so many things work pretty much the same as in Bash. In
your case, command substitution is done with
echo "Foo $(./print_5_As.rb)"
in both PowerShell and Bash.
Bash still supports the ancient way (backticks), but
PowerShell cleaned up the syntax and removed redundant
constructs such as the two different command substitution
syntaxes. This frees up the backtick for a different
use in PowerShell: in POSIX ksh, the backslash is used as
escape character, but that would be very painful in
PowerShell because the backslash is the traditional path
component separator in Windows. So, PowerShell uses the
(now unused) backtick for escaping.
In PowerShell, you use $( ) to evaluate subexpressions...
For example:
PS C:\> "Foo $(./print_5_As.rb)"
Foo AAAAA
In CMD.EXE there is no direct equivalent. But you can use the FOR command to achieve what you want.
Do something like the following:
FOR /F "usebackq" %x IN (`./print_5_As.rb`) DO #echo Foo %x
or
FOR /F %x IN ('"./print_5_As.rb"') DO #echo Foo %x
You might need to set delimiter to something else than the default, depending on how the output looks and how you want to use it. More details available in the FOR documentation at https://technet.microsoft.com/en-us/library/bb490909.aspx

Resources