8

I want to set a variable e.g. %p% and then use it in the same line in CMD.

e.g.:

set p=notepad.exe&%p%

This not works. But %p% is set for the next line. Therefore if I execute this line for second time, it works.

How can I use %p% in the same line?

A.Danesh
  • 844
  • 11
  • 40
  • If your purpose is opening some exe e.g notepad or some file, you can do it directly in single line, exactly what do you want with this variable? – ZAT Oct 08 '14 at 07:39
  • @ZAT It is only as an example. My purpose is to convert a complicated batch file to a single line. All of code is converted except setting a variable, like above. I don't want to execute batch file directly. – A.Danesh Oct 08 '14 at 07:41
  • 1
    If you are storing complex code in variables, then you may be interested in [Batch "macros" with arguments](http://www.dostips.com/forum/viewtopic.php?f=3&t=1827) – dbenham Oct 08 '14 at 11:21

1 Answers1

12

When you execute a batch file, each line or block of lines (lines enclosed in parenthesis) is first parsed and then executed. During the parse, the read operations on the variables are replaced with the value inside the variable before the commands are executed. So if in a line/block a variable value is changed, you can not retrieve this changed value as the parser has removed the read operation with the value in the variable before the change has been made.

So, your code

set p=notepad.exe&%p%

is parsed and converted to

set p=notepad.exe&

where the read operation to get %p% has been replaced with the value in the variable (the sample assumes the variable is empty). Then this parsed line is executed.

Why does it work the second time? Because in the previous run the variable has been set and, if it has not been reset, when the parser do the replacement in the second run the variable contains a value to replace in the line.

To see that this is a parse before execute behaviour, you can change your line to

set p=notepad.exe&set p

that is, set the variable and dump the environment content (variables starting with p) and you will see that the variable has been set to notepad.exe. As this line does not contain any read operation on the variable everything works as expected.

How to solve your problem? There are some options

Delayed expansion

When delayed expansion is enabled, the syntax on variable reads can be changed, where needed, from %var% to !var!, indicating to the parser that the substitution on the read operation needs to be delayed until the command execution

setlocal enabledelayedexpansion
set p=notepad.exe&!p!

This same behaviour can be enabled if the command processor is called with /v:on

cmd /v:on /c "set p=notepad.exe&!p!"

Force a second parse on the command

This uses the call command to force a second parse on the line

set p=notepad.exe&call %%p%%

The first default parse replaces the literal %%p%% with the literal %p% (%% is a escaped percent sign) without doing any variable replacement, and when the call command is executed, the line is parsed again, so the literal %p% is interpreted as a variable read and is replaced with the value in the variable

What to use? It depends. Delayed expansion solution has better execution times that call solution (call command does a lot more work), but when delayed expansion is enabled exclamation characters (!) contained in the data become a problem that needs to be properly handled.

MC ND
  • 69,615
  • 8
  • 84
  • 126
  • 3
    +1, Note that CALL is very slow. So if performance is an issue, then delayed expansion is preferable to CALL – dbenham Oct 08 '14 at 11:16