I'd like to do this:
set %1="html"
But it doesnt work, I get back:
'"html"' is not recognized as an internal or external command,
operable program or batch file.
%1 is not a variable that you assign with SET. It represents the first argument to a called batch script (test.bat arg1 or call test.bat arg1) or subroutine (call :subroutine arg1).
If you want to change the value of %1, then you do so where you make the CALL, not within the CALLed script/routine.
There are situations where a line set %1=value is used. You might have a routine that computes a value and then stores the result in a variable whose name is retrieved from the first argument to the routine.
But if the routine is called without an argument, then the line expands to set =value, which generates the error that you are getting.
If you are working with the %1 argument, and conditionally want to "change the value" within the script, then you must transfer the value to an environment variable, and work with the variable from that point on.
A trivial example would be to provide a default value if the argument is not passed:
#echo off
setlocal
set "arg1=%~1"
if not defined arg1 set "arg1=default"
REM From now on you never refer to `%1` but use `%arg1%` instead.
Take a look at this:
#ECHO OFF
Setlocal EnableDelayedExpansion
SET 1="html"
echo %1%
echo !1!
PAUSE
Executing this code with parameter abc will return
abc
html
Obviously you need Setlocal EnableDelayedExpansion to be able to "overwrite" %1. However, this won't actually overwrite %1 but make it's updated value accessable. AFAIK there is no way to really overwrite it. There is only one workaround I can think of:
Call your file with "html" as second parameter and use SHIFT:
ECHO %1
SHIFT
ECHO %1
Executing this code with params X and html (yourfile.bat X html) will print:
X
html
Related
I've recently been learning a bit about batch scripting and have spent some time the past few days creating different .BAT files to hone my skills and gain further understanding about how they work and the many things they can be used to accomplish.
Today, I was curious whether or not I could effectively call on batch script from another, so I created two batch scrips: test.bat and test2.bat. The code for each is as follows:
TEST.BAT
#echo off
cls
title test.bat
set /p "_result=Enter 'c' for capture or 'd' for deploy:"
call test2.bat %_result%
TEST2.BAT
if "~%1" == "c" goto :capture
if "~%1" == "d" goto :deploy
:capture
echo _result is %_result%
echo %1
echo You are in the capture label!!
goto :eof
:deploy
echo _result is %_result%
echo %1
echo You are in deploy label!!
goto :eof
I have a few questions related to my above code that I think largely have to do with semantics, but that I also think will help me understand more completely what exactly is happening in this simple exercise.
Question 1:
When I pass the _result variable to test2.bat, is the _result variable now considered a parameter that's being passed to test2.bat? In other words, is it proper to say in this case that I am passing a parameter or, since it is a variable, should I say I'm passing a variable? Or is it both?
I have attempted to figure this out on my own, which is why I've included the echo _result is %_result% and echo %1 in my code. They both echo the same response, so I assume it would be okay to refer to it either way, but please correct me if I'm wrong.
Question 2:
Since it appears (to my current understanding of this) that _result holds it's variable attribute and value when passed to test2.bat, does this mean that it should be considered a global variable?
Question 3:
I'm a little confused by why it retains the same value in test2.bat that it was assigned in test.bat since they are two completely different files (especially if it's just considered a parameter once it's passed). Is test2.bat just considered an extension of sorts of test.bat in this case? And therefore all the variables designated in test.bat would be active in test2.bat?
I hope my questions make sense. Please let me know if I need to clarify anything. Thank you for your help.
_result variable is set in the environment test.bat is called in, and that's why test2.bat can access it (and modify it), and also why you can echo _result after test.bat is finished.
When you call test2.bat %_result% you are just passing in the value of _result to test2's first argument (read by %1).
You can add setlocal at the start of test.bat to get the environment variables back where they were when you're done.
Question 1
You are not passing the variable, you are merely passing its value. But the two batch scripts share the same environment context, hence the variable is also available in the called one.
Question 2
Yes, you can call it global in the environment context.
To localise environments use setlocal/endlocal, like in the following example:
caller.bat:
#echo off
set "VAR=foo"
echo CALLER: Variable before call: "%VAR%"
call callee.bat "%VAR%"
echo CALLER: Variable upon return: "%VAR%"
exit /B
callee.bat:
setlocal
echo CALLEE: Passed argument: "%~1"
echo CALLEE: Original variable: "%VAR%"
set "VAR=bar"
echo CALLEE: Modified variable: "%VAR%"
endlocal
exit /B
When running caller.bat, the output is going to be:
CALLER: Variable before call: "foo"
CALLEE: Passed argument: "foo"
CALLEE: Original variable: "foo"
CALLEE: Modified variable: "bar"
CALLER: Variable upon return: "foo"
If you removed setlocal and endlocal from callee.bat, the last line would show bar.
Question 3
Yes, you can think of the callee as it was embedded in the caller, with respect to the environment. When you do that with the above example scripts you will see the result is exactly the same.
But:
When it comes to block context, meaning parenthesised ( blocks of code ) or concatenated & commands, simply copying the callee into the caller does not work. This has nothing to do with the environment but with the way variables are expanded (read).
If call appears in a code block (like the body of a for loop for example), the callee is not executed in the block context of the caller, it starts a new block context. As soon as the callee terminates, the block context of the caller is restored. Let us take the above examples and modify them a bit:
caller.bat:
#echo off
set "VAR=foo"
(
echo CALLER: Variable before call: "%VAR%"
call callee.bat "%VAR%"
echo CALLER: Variable upon return: "%VAR%"
)
exit /B
callee.bat:
echo CALLEE: Passed argument: "%~1"
echo CALLEE: Original variable: "%VAR%"
set "VAR=bar"
echo CALLEE: Modified variable: "%VAR%"
exit /B
The call command line in the caller is now placed inside of a parenthesised block, the callee does not contain environment localisation (setlocal/endlocal), so one might expect the last returned line to show bar. But in fact it does not:
CALLER: Variable before call: "foo"
CALLEE: Passed argument: "foo"
CALLEE: Original variable: "foo"
CALLEE: Modified variable: "bar"
CALLER: Variable upon return: "foo"
As soon as the last echo command line in the caller is moved after the closing ), the output holds bar. As said, all this has got nothing to do with the environment. This is caused by the fact that the entire (/) block is read at once and all variables are expanded (meaning replaced by their values) immediately, hence modification of variables is not reflected within that block. You can simply prove this by typing set "VAR=value" & echo "%VAR%" into the command prompt: the first time executed, the result will be "", because the &-concatenated line/block is read at once and %VAR% is immediately replaced by its current value, which is nothing at the first time, then execution of the commands start; when you execute it a second time, VAR already contains the string value from the beginning, so you will receive value as the result. This way of reading variables is often referred to as normal or immediate expansion, or also to percent expansion due to the enclosing %% signs of the variable.
There is a way to change this behaviour to something closer to other (real) programming languages, namely delayed expansion. Using that, you can assign or change a variable and access its value immediately, even in blocks. At first this feature needs to be enabled by the setlocal command (or alternatively, in command prompt, by the /V switch of cmd). Then it has to be actually used by enclosing the variable in a pair of exclamation marks, like !VAR!. Note that you can still use percent expansion when delayed expansion is enabled.
To apply that to the above example, it may look like that:
caller.bat:
#echo off
setlocal EnableDelayedExpansion
set "VAR=foo"
(
echo CALLER: Variable before call: "%VAR%"
call callee.bat "%VAR%"
echo CALLER: Variable upon return, but immediate expansion: "%VAR%"
echo CALLER: Variable upon return, using delayed expansion: "!VAR!"
)
endlocal
exit /B
callee.bat:
echo CALLEE: Passed argument: "%~1"
echo CALLEE: Original variable: "%VAR%"
set "VAR=bar"
echo CALLEE: Modified variable: "%VAR%"
exit /B
The output would now be:
CALLER: Variable before call: "foo"
CALLEE: Passed argument: "foo"
CALLEE: Original variable: "foo"
CALLEE: Modified variable: "bar"
CALLER: Variable upon return, but immediate expansion: "foo"
CALLER: Variable upon return, using delayed expansion: "bar"
Note: Although the block context has absolutely nothing to do with the environment, I still wanted mention this, because it affects the way variables are expanded/read.
This might be a simple question, but I didn't found any thread, may be I am searching wrong.
I am having a simple bat script and passing some arguments. Currently I am getting the argument based on there order. But I want to make simple for the user.
My current command line looks like: testBatch.bat setupip setupuser setuppass
My current batch script looks like:
set setupip=%1
set setupuser=%2
set setuppass=%3
echo I am doing some operation after this based on the input I got.
Now I want to modify the command such that the user should not be woried about the order of argument to pass. Basically I want to see my command like this: testBatch.bat --ip setupip --user setupuser --password setuppass
What modification I have to do in my batch script?
try this:
#echo off
for %%a in (%*) do (
call set "%%~1=%%~2"
shift
)
echo :%--user%
echo :%--pass%
if the passed it is called like:
namedArgs.bat --user user --pass pass
the output will be like:
:user
:pass
This will handle also quoted parameters with spaces in them.
Thing you are looking for is called 'Named Arguments'. I found link for same question here.
Answer Which I like from that link is as follow:
set c=defaultC
set s=defaultS
set u=defaultU
:initial
if "%1"=="" goto done
echo %1
set aux=%1
if "%aux:~0,1%"=="-" (
set nome=%aux:~1,250%
) else (
set "%nome%=%1"
set nome=
)
shift
goto initial
:done
echo %c%
echo %s%
echo %u%
Run the following command:
arguments.bat -c users -u products
Will generate the following output:
users
defaultS
products
There is a batch command called shift, which can basically do a shift left on your parameter variables.
So as the start of your script, you need a loop
inside that loop you decode %1 for the 'verb' part of your option with a if statement
inside that if block, you call shift. then old %2 becomes the new%1, old %3 becomes the new %2, etc
so inside that if, you can pick up a secondary argument for the verb, if needed. Then call shift again.
loop until your arguments are empty. Tadah!
I've found what appears to be an explanation of my problem here
DOS batch: Why are my set commands resulting in nothing getting stored?
but I don't really understand the explanation.
Here is my script...
for /R /d %%f in (\Product\Database\SQL\Project\Model\Scripts\*) DO (
REM echo %%f
SET !LOAD_FILE_FILTER= %%f\*.sql
echo file: %!LOAD_FILE_FILTER%
CALL %!BATCH_FILE% -u %!USER_NAME% -p %!PASSWORD% -s %!SERVER_NAME% -d %!DATABASE_NAME% -f %!LOAD_FILE_FILTER% -o %!LOG_FILE%
IF %!EchoErrors%==1 (
ECHO [
TYPE %!LOG_FILE%
ECHO ]
)
)
The echo always prints file: *.sql and the script I pass this var to always complains LOAD_FILE_FILTER is empty.
I have tried adding setlocal EnableDelayedExpansion as suggested in the article but it doesn't solve the problem. The echo file: %!LOAD_FILE_FILTER% always prints the last subdirectory in the directory I'm running from. The echo %%f always prints the correct value.
What does the '!' behind the variable do for/to me?
On a side note, could someone explain to me the difference between
SET !VAR
and
SET VAR
%VAR&
&!VAR&
!VAR!
%%VAR
We are going to start with a simple case
set "var="
set "var=test"
echo %var%
Reading the code, it removes the content of the variable, assigns it a new value and echoes it.
Let's change it a bit concatenating the last two commands
set "var="
set "var=test" & echo %var%
"Same" code, but in this case the output to console will not show the value in the variable.
Why? In batch files, lines to execute are parsed and then executed. During the parse phase, every variable read operation (where you retrieve the value of the variable) is replaced with the value stored inside the variable at parse time. Once this is done, the resulting command is executed. So, in the previous sample when the second line is parsed, it is converted to
set "var=test" & echo
now, there are no read operations on the line and no value to echo, as when the line was readed the variable didn't hold any value (it will be assigned when the line is executed) so the read operation has been replaced with nothing. At this point, the code is executed and the perceived behaviour is that the set command failed as we don't get the "obvious" value echoed to console.
This behaviour is also found in blocks. A block is a set of lines enclosed in parenthesis (usually for and if constructs) and are handled by the parser as if all the lines in the block are only one line with concatenated commands. The full block is readed, all variable read operations removed and replaced with the value inside the variables, and then the full block, with no variable references inside is executed.
At execution time there are no read operation on variables inside the block, only its initial values, so, any value assigned to a variable inside the block can not be retrieved inside the same block, as there isn't any read operation.
So, in this code
set "test=before"
if defined test (
set "test=after"
echo %test%
)
after the first set is executed, the block (the if command and all the code enclosed in its parenthesis) will be parsed and converted into
if defined test (
set "test=after"
echo before
)
showing the "wrong" value.
The usual way to deal with it is to use delayed expansion. It will allow you to change, where needed, the syntax to read the variable from %var% into !var!, indicating to the parser that the read operation must not be removed at parse time, but delayed until the command is executed.
setlocal enabledelayedexpansion
set "var="
set "var=test" & echo !var!
The now third line is converted at parse time to
set "var=test" & echo !var!
yes, the variable reference is not removed. The read operation is delayed until the echo command will be executed, when the value of the variable has been changed.
So
%var% is a variable reference that will be replaced at parse time
!var! is a variable reference that will be replaced at execution time
%x with x a single character is usually a for replaceable parameter, a variable that will hold the current element being interated. By its own nature, will be expanded at execution time. The syntax with a single percent sign is used at command line. Inside batch files the percent sign need to be escaped and the syntax to refer to the replaceable parameters is %%x
I am writing a batch library that must not be local, so that external batch files may call upon the functions from a single defined location.
This requires that the calling batch file must pass in a "unique" identifier for the variables named within. (I.E. pass in Application1 for %1).
I then wish to dynamically name variables, such as:
set %1_Timer=Hello
This works well, except I need to be able to evaluate said dynamic variables, but cannot find a solution that allows me to evaluate these parameter based dynamic variables.
I've tried solutions such as:
echo %1_Timer%
echo %1_Timer
echo %%1_Timer%%
echo %%1_Timer%
Call echo %%1%_Timer%
I cannot use a variable that is not dynamically named as other scripts utilizing this library might alter that non-dynamic variable, altering the output for other scripts.
You want to use delayed expansion.
#echo off
setlocal enabledelayedexpansion
echo !%1_Timer!
You could also use call and %% signs (call echo %%%1_Timer%%, but generally delayed expansion will be more useful. Also, it is mandatory if you ever set or modify a variable inside of a code block like a for or an if.
#ECHO OFF
SETLOCAL
SET myapp_timer=original_value
CALL :appsee myapp
CALL :appset myapp
CALL :appsee myapp
GOTO :EOF
:: set variable(s) - can be external batch to set caller's environment
:appset
SET %1_timer=hello
GOTO :EOF
:: see variable(s) - can be external batch to set caller's environment
:appsee
CALL ECHO %%1_timer=%%%1_timer%%
GOTO :EOF
Here's how.
this is my code :
SET JAVA_VERSION=%1
REM here lets assume the user enter a JAVA_7_HOME as an argument
#ECHO %JAVA_VERSION%
REM this print me JAVA_7_HOME because JAVA_7_HOME is STRING
REM Now I want to access to the value of environment variable JAVA_7_HOME so I done this :
if %%%JAVA_7_HOME%%% ==[]GOTO INDEFINED_VARIABLE
REM here I concatinate JAVA_7_HOME with % % at the left and at the right but doesn't works
I Do not Know how to access to the value of the environment variable JAVA_7_HOME.
Thank you at all.
CALL SET JAVA_VERSION=%%%1%%
The line will be parsed to
CALL SET JAVA_VERSION=%JAVA_7_HOME%
Now, the call command is executed and the line reparsed, and the final command executed is
SET JAVA_VERSION=C:\SomeWhere
Also, you can enable delayed expansion and do
SETLOCAL EnableDelayedExpansion
SET JAVA_VERSION=!%1!
I backed to this subject :), So your solution it works fine without a loop for but now I changed the concept and I have a variable called CONTEXT_VARAIABLE which contains some others names of environnements variables for example in my case is:
CONTEXT_VARAIABLE=JAVA_HOME;JBOSS_HOME
so now I loop from this variable CONTEXT_VARAIABLE to get each variable and do some treatement with it so my code here is:
setlocal EnableDelayedExpansion
FOR %%L IN (%SOLIFE_VERSION%) DO (
SET JAVA_JBOSS_HOME=%%%%L%%
#ECHO !JAVA_JBOSS_HOME!
)
here each time of the loop it prints me:
%JAVA_HOME%
%JBOSS_HOME%
and now my problem I want to access to these variables and if I use CALL SET it makes an error because I think we are not allowed to use CALL SET in loop for.
So please if you have an idea about this problem.
Thank you.