Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows command prompt: Using a variable set in the same line of a one-liner

Okay, I learned that one can use & or && to combine multiple commands into a single line, but it seems that variables that are set aren't actually available for interpolation in the same line:

C:\Users\Andrew>set foo=hello, world!&& echo %foo%
%foo%

C:\Users\Andrew>echo %foo%
hello, world!

Why can't I make this work, and is there any way to make it work in a single line?

The reason I need a one-liner is that an external program I'm working with accepts a single command as a pre-run hook, and, of course, I need to run multiple commands.


Preemptive Defenses

  1. "hello, world! should be surrounded in double-quotes!" Actually, doing so seems to store literal double-quotes in the variable, which I do not want, e.g.

    C:\Users\Andrew>set bar="hello, world!"&& echo %bar%
    %bar%
    
    C:\Users\Andrew>echo %bar%
    "hello, world!"
    
  2. "There should be a space before the &&!" Actually, doing so seems to store a trailing space in the variable, which I do not want, e.g.

    C:\Users\Andrew>set bar="hello, world!"&& echo %bar%
    %bar%
    
    C:\Users\Andrew>echo %bar%
    "hello, world!"
    
  3. "Both!" >:(

    C:\Users\Andrew>set mu="hello, world!" && echo %mu%
    %mu%
    
    C:\Users\Andrew>echo (%mu%)
    ("hello, world!" )
    
like image 834
slackwing Avatar asked May 02 '14 03:05

slackwing


People also ask

How do I run a variable from the command line in Windows?

To reference a variable in Windows, use %varname% (with prefix and suffix of '%' ). For example, you can use the echo command to print the value of a variable in the form " echo %varname% ".

How do I pass a variable in command prompt?

To pass variable values via the command line, use the PrjVar ( pv ) or PSVar ( psv ) arguments for the project and project suite variables respectively. Variable values you pass via the command line are temporary.


3 Answers

You can do it in the same line, but I would recommend to use a batch file like preHook.bat.

As one liner

set "mu=hello, world!" && call echo %^mu%

At first you can see that the quotes are different than yours.


Here's why:

set test1="quotes"
set "test2=no quotes" with comment

In the first test, the quotes will be part of the test1 variable, as well as all characters after the last quote.

In the second test, it uses the extended syntax of the SET command.
So the content will be no quotes, as only the content to the last quote is used; the rest will be dropped.

To echo the variable content, I use call echo %^mu%, as percent expansion will expand it when the line is parsed, before any of the commands are executed.

But the call command will be executed later, and it restarts the parser, which, when used at the command line, uses different expansion rules than the batch parser: an empty variable (in this case, %^mu%, in the first time) stays unchanged in the line; but, in the next parser phase, the ^ caret will be removed.

In your case, call echo %mu% would also work, but only when mu is always empty. The caret variant also works when mu has content before the line is executed.

More about the parser at SO: How does the Windows Command Interpreter (CMD.EXE) parse scripts? And about variable expansion at SO: Variable expansion

like image 158
jeb Avatar answered Oct 21 '22 14:10

jeb


Though I accepted @jeb's answer, ultimately I had to go another route, because I needed to pipe the output of my command, and doing so on call led to mangled results. Indeed, the documentation for call itself remarks,

Do not use pipes and redirection symbols with call.

Instead, reminded by @Blorgbeard of cmd's /v option (and I believe that should be lowercase, not uppercase), I realized I could simply start a subprocess:

C:\Users\Andrew>cmd /v /c "set foo=hello, world!&& echo !foo!"
hello, world!

(For some reason, /v must appear before /c.) Within the quotes, I was able pipe my output to other utilities. One tip for those taking this path: If you find yourself needing to use quotes within those quotes, I suggest avoiding them altogether and trying character codes, e.g. \x20 for space, \x22 for double quotes, and so on.

For example, this was the eventual solution to my problem (warning: may cause eyes to bleed):

C:\Users\Andrew>cmd /v /c "set source=C:\source& set target=C:\target& set archive=C:\archive& robocopy.exe !source! !target! /l /e /zb /xx /xl /fp /ns /nc /ndl /np /njh /njs | sed -e s/^^[\t\x20]\+// | sed -e /^^$/d | sed -e s/^!source:\=\\!// | sed -e s/.*/xcopy\x20\/Fvikrhyz\x20\x22!source:\=\\!^&\x22\x20\x22!archive:\=\\!^&\x22/"
like image 7
slackwing Avatar answered Oct 21 '22 16:10

slackwing


Try the following:

cmd.exe /v /c "set foo=hello, world & echo !foo!"

The /v argument enables delayed variable expansion. This allows you to access variables' values at execution time rather than at parse time (the default). You do this by writing !foo! instead of %foo% (with this /v option turned on).

Instead of passing /v, you can also turn delayed variable expansion on permanently via the registry, which obviously affects the entire system:

[HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor]
"DelayedExpansion"= (REG_DWORD)
1=enabled 0=disabled (default)
like image 5
Blorgbeard Avatar answered Oct 21 '22 15:10

Blorgbeard