How to export a variable out of block that relates a pipeline? - dos

I got a value from pipeline to a variable dd1.
But i can not export a variable out of block.
How to export a variable out of block that relates a pipeline ?
Example batch script :
#echo off
(date /t) | (set /p zDate=& call set zDate=%%zDate:~0,4%% & call echo
Possible: %%zDate%% )
echo ImPossible: %zDate%
pause >nul
exit 0
Result :
-------------------------------------------
Possible: 2015
ImPossible:
Why not export a variable zDate out of Block ?
please answer to me.
Thank you.

Well once a variable within a pipe block is ended, the environment changes to the default file and the variable is lost. The only workaround I can think of is to export the variable into a temp file which is read by the default batch after the pipe ends. Perhaps:
#echo off
(date /t) | (set /p zDate=& call set zDate=%%zDate:~0,4%% & call echo Possible: %%zDate%% & call echo %%zDate%%^>%temp%\zDate.txt )
set /p zDate=<%temp%\zDate.txt
echo Impossible: %zDate%
pause >nul
exit 0
Is what you intended? If not please let me know and I'll update the answer!
_Arescet

Related

Update variable of batch script using set /p always contains last value

I'm trying to adjust a Windows batch variable within a loop using the set /p command. After the keyboard input the variable still contains the old value. I have read that the variable set via set /p has only local scope. But I do not understand what "local" really means here.
#echo off
setlocal EnableDelayedExpansion
set a=4
echo Inital A: %a%
:LoopLabel
MODE | find %a% >nul 2>&1
IF %ERRORLEVEL% NEQ 0 (
set /p "a=enter new a: "
echo a=%a%
goto LoopLabel
)
The output is:
Inital A: 4
enter new a: 5
a=4
enter new a: 6
a=5
enter new a: 7
a=6
Does anyone have an idea and can me explain why this is happening?
Many Thanks,
Jonny
Executing code inside the IF is the problem. Check this out:
#echo off
set a=4
echo Inital A: %a%
:LoopLabel
MODE | find "%a%" >nul 2>&1
IF %ERRORLEVEL% EQU 0 goto stuff
set /p "a=enter new a: "
echo a=%a%
goto LoopLabel
:stuff
The set /p is executed outside of the IF, so it works fine even without delayed expansion.
All of the commands inside the IF block get evaluated at the same time (in parallel), so when
echo a=%a%
runs, it is unaware of any new value assigned by
set /p "a=enter new a: "
To further clarify, your a does in fact contain the new value, but the echo a=%a% was executed using the old value.
One solution is to use IF and labels to get the expected program flow, while avoiding multi-step execution within an IF block.
Also you're going to get an error from find unless either your entered value is quoted or you add quotes around %a% when you feed it to find.
Alternatively you can use delayed expansion on your a value to get its value "after" set /p has changed it.
To do this, you would change
echo a=%a%
to
echo a=!a!
The only variable scope that I know of in batch is the %1...%9 and %* in called labels. Otherwise everything is global, including your a set by set /p.

How to check if a variable exists in a batch file?

I am using the call command:
call beingcalled.bat randomnumber
In beingcalled.bat:
#echo off
set call=%1
echo %call%
set call=%call%%call%
call caller.bat %call%`
In caller.bat:
#echo off
set calltwo=%1
echo %calltwo%
if "%calltwo%"== "" (
echo Error
) else (
call beingcalled.bat randomnumber
)
Why does the command if "%calltwo%"== "" not work? And how do I see if a variable was set?
IF "%Variable%"=="" ECHO Variable is NOT defined
This should help but this works, provided the value of Variable does not contain double quotes.
Or you may try. Both worked for me.
VERIFY OTHER 2>nul
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 ECHO Unable to enable extensions
IF DEFINED MyVar (ECHO MyVar IS defined) ELSE (ECHO MyVar is NOT defined)
ENDLOCAL
source http://www.robvanderwoude.com/battech_defined.php
The easiest way is just using the command line extension DEFINED. This is also my preferred way of doing this.
in your case:
#echo off
set calltwo=%1
echo %calltwo%
if defined calltwo (
echo Error
)else (
call beingcalled.bat randomnumber
)
If this doesn't work for you there is a workaround in the link below.
The question is also a duplicate of: Check if an environment variable is defined without command extensions and without using a batch file?
This is just a follow-up to the comment (and bounty) post by #Rishav
Here’s a trick I picked up a very long time ago:
#ECHO OFF
SET Foo=%1
ECHO == Start ====================
ECHO %Foo%
IF %Foo%x == x ECHO Parameter not set
ECHO == End ====================
ECHO.
If the parameter is not set, you get a check of x==x
If the parameter is set (to, say, “asdf”), you get a check of asdfx==x
None of these solutions work for me on windows 10, but I did find a solution that does work
IF %foo%==^%foo^% ECHO variable not defined

Windows command line: why environment variable is not available after &

Have a look at the following commands: why is the value of a not available immediately after the &?
C:\>set a=
C:\>set a=3&echo %a%
%a%
C:\>echo %a%
3
C:\>set a=3&echo %a%
3
But when I do
C:\>set a=
C:\>set a=3&set
a=3 is included in the listed variables!
I need this for a trick I learned here, to get the exit code of a command even output is piped:
Windows command interpreter: how to obtain exit code of first piped command
but I have to use it in a make script, that's why everything must be in one line!
This is what I am trying to do:
target:
($(command) & call echo %%^^errorlevel%% ^>$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & exit /B %errorlevel_make%
But errorlevel_make is always empty (the file with the exit code exists and contains the correct exit code).
Is this a bug in cmd? Any ideas what I can do?
The reason for the observed behaviour is how the command line is processed.
In first case set a=3&echo %a%, when the line is interpreted BEFORE EXECUTING IT, all variables are replaced with their values. a has no value until line executes, so it can not be substituted in echo
In second case, set a=3&set, there is no variable substitution before execution. So, when set is executed, a has the value asigned.
It's much easier to create a seperate batch file to solve this, as it isn't obvious even for experts.
But in your case this should work
target:
($(command) & call echo %^^^^errorlevel% >$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & call exit /B %^errorlevel_make%
A seperate batch could look like
extBatch.bat
#echo off
("%~1" & call echo %%^^errorlevel%% > "%~2") 2>&1 | tee "%~3" & set /p errorlevel_make=<"%~2"
exit /B %errorlevel_make%
Then you could start the batch from your make file
target:
extBatch.bat $(command) $(exitcodefile) $(logfile)
Inspired by the nice answer of jeb, I modified the code a little to be able to grab errorlevel for each side of the pipe, separately. For example you have the following pipe in your batch file:
CD non_exisitng 2>&1 | FIND /V ""
ECHO Errorlevel #right: %errorlevel%
The errorlevel above refers to the right-hand side of the pipe only, and it indicates if the FIND command succeeded of not.
However, to be able to get the errorlevel of both sides, we can change the above code to something like this:
(CD non_exisitng & CALL ECHO %%^^errorlevel%% >err) 2>&1 | FIND /V ""
ECHO Errorlevel #right: %errorlevel%
SET /P err=<err
ECHO Errorlevel #left: %err%
output:
The system cannot find the path specified.
errorlevel #right: 0
errorlevel #left: 1
#OP:
I realize this comes a bit late, but might be helpful for others. In addition to jeb's method of an external batch file, the original problem can also be solved as one-liner:
target:
($(command) & call echo %%^^errorlevel%% >$(exitcodefile)) 2>&1 | tee $(logfile) & set /p errorlevel_make=<$(exitcodefile) & cmd /c call exit /B %%errorlevel_make%%

Find and Replace inside for loop [batch script]

The below code works, echo test.test
set replaceWith=.
set str="test\test"
call set str=%%str:\=%replaceWith%%%
echo %str%
But, the below code echo ggg.hhhhh all the 4 times.
SET SERVICE_LIST=(aaa\bbb ccc\dddd eeee\fffff ggg\hhhhh)
for %%i in %SERVICE_LIST% do (
set replaceWith=.
set str="%%i"
call set str=%%str:\=%replaceWith%%%
echo %str%
)
What am I doing wrong here?
If you understand why your code uses call set str=%%str:\=%replaceWith%%%, then you should be able to figure this out ;-)
Syntax like %var% is expanded when the line is parsed, and your entire parenthesized FOR loop is parsed in one pass. So %replaceWith% and echo %str% will use the values that existed before you entered your loop.
The CALL statement goes through an extra level of parsing for each iteration, but that only partially solves the issue.
The first time you ran the script, you probably just got "ECHO is on." (or off) 4 times. However, the value of str was probably ggghhhhh and replaceWith was . after the script finished. You don't have SETLOCAL, so when you run again, the values are now set before the loop starts. After the second time you run you probably got ggghhhhh 4 times. And then from then on, every time you run the script you get ggg.hhhhh 4 times.
You could get your desired result by using CALL with your ECHO statement, and moving the assignment of replaceWith before the loop.
#echo off
setlocal
SET SERVICE_LIST=(aaa\bbb ccc\dddd eeee\fffff ggg\hhhhh)
set "replaceWith=."
for %%i in %SERVICE_LIST% do (
set str="%%i"
call set str=%%str:\=%replaceWith%%%
call echo %%str%%
)
But there is a better way - delayed expansion
#echo off
setlocal enableDelayedExpansion
SET "SERVICE_LIST=aaa\bbb ccc\dddd eeee\fffff ggg\hhhhh"
set "replaceWith=."
for %%i in (%SERVICE_LIST%) do (
set str="%%i"
set str=!str:\=%replaceWith%!
echo !str!
)
Please have a text book for Windows Command Shell Script Language and try this:
#ECHO OFF &SETLOCAL
SET "SERVICE_LIST=(aaa\bbb ccc\dddd eeee\fffff ggg\hhhhh)"
for /f "delims=" %%i in ("%SERVICE_LIST%") do (
set "replaceWith=."
set "str=%%i"
SETLOCAL ENABLEDELAYEDEXPANSION
call set "str=%%str:\=!replaceWith!%%"
echo !str!
ENDLOCAL
)

How to include a value from an external bat script

I have a code that is shared by 6 different bat scripts below that takes an input argument. I wonder if I can externalize this piece in a seperate bat script and import it instead, so everytime I update this piece of code, I don't have to update all 6 bat scripts.
Code:
:Loop
IF "%1"=="" GOTO Prompt
SET VAR=%1
GOTO Continue
SHIFT
GOTO Loop
:Prompt
set /p VAR="Check which value? "
GOTO Continue
:Continue
Yes, using redirection.
Take this solution.bat file
set /p myvar=< somestring.txt
Where somestring.txt contains "abc"
myvar will now exist as an environment variable with abc.
Your code is supposed to set VAR to the first argument. If the first arg is missing then you want to prompt for a value.
First off, I would simplify your logic.
set "VAR=%~1"
if not defined VAR set /p "VAR=Check which value? "
Once simplified like above, I don't see why you would feel a need to externalize the code. But it could be done.
In your main script
call getArg.bat %1
And here is getArg.bat
set "VAR=%~1"
if not defined VAR set /p "VAR=Check which value? "
exit /b

Resources