Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Batch file not keeping variables

Why doesn't the below batch file keep variables when CMD.exe terminates

@echo off
cmd /c "set var=hi"
if defined var (echo var is defined ("var=%var%")) else (echo var isn't defined)
pause
exit

Is there any way to use CMD /c, whilst keeping variables? and why doesn't CMD /c keep variables?

like image 308
Burnie Riley Avatar asked Oct 19 '22 01:10

Burnie Riley


1 Answers

CMD /C starts a new sub-process that has its own environment space where variables are stored in memory. When that process terminates, the associated environment space is lost, so of course you lose the definition of any variables that were defined there.

It is not possible for the sub-process to directly access or manipulate the variables of the parent process.

If you are executing a simple command and have control over any output, then you can write the definitions to stdout and let FOR /F capture the output so that you can set the values in your parent process. But you will need delayed expansion.

Note - this example is pretty inane, but it should get the point across

for /f "delims=" %%A in ('cmd /v:on /c "set var=hi&echo "var=!var!""') do set %%A

But things can get complicated as far as identifying what is quoted, what needs to be escaped, ...

Life can be simplified if you create a batch script that does the work and execute it with FOR /F.

test.bat

@echo off
set var1=hi
set var2=Ain't this fun
set var3=bye
setlocal enableDelayedExpansion
for %%V in (var1 var2 var3) do echo "%%V=!%%V!"

main.bat

@echo off
for /f "delims=" %%A in ('cmd /c test.bat') do set %%A

Actually, FOR /F uses an implicit CMD /C, and you no longer need to enable delayed expansion in the command, so you can ditch the explicit CMD /C

@echo off
for /f "delims=" %%A in ('test.bat') do set %%A

If you don't have control of the output, then you will have to write the values to a temporary file that the main script can then load. You might be tempted to write a temporary batch script that defines the variables, but then you run the risk of having to escape some characters, depending on the content of the variables. The safest option is to use delayed expansion to write out the variable names and values, and let the parent process use FOR /F to load them in.

test2.bat

@echo off
set var1=hi
set var2=Ain't this fun
set var3=bye
echo Here is some output that must be preserved, unrelated to variables
setlocal enableDelayedExpansion
(for %%V in (var1 var2 var3) do echo "%%V=!%%V!") >"%temp%\vars.temp"

main.bat

@echo off
setlocal disableDelayedExpansion
cmd /c test2.bat
call "%temp%\vars.bat"
for /f "usebackq delims=" %%V in ("%temp%\vars.temp") do set "%%V"
del "%temp%\vars.temp"

Note that delayed expansion must be disabled in the main process when you load the variables, otherwise you will lose any ! characters in values when %%V is expanded.

But to be robust, you should do something to make sure that each instantiation uses a unique temp file name so that simultaneous runs do not clobber eachother.

like image 119
dbenham Avatar answered Oct 21 '22 04:10

dbenham