I have a variable assignment problem inside the DOS script for loop. It never assigns the value, its always blank. Below the sample code
#echo off
set ans=%1
SET STRING=%ans:"=%
echo Parsing the string "%STRING%":
for /f "tokens=1-2" %%a in ("%STRING%") do (
set Word1 =%%a
echo Word 1: %%a
echo Word 1: %Word1%
set Word2 =%%b
if %%b.==. (Set server =\\.\pipe\mssql$microsoft##ssee\sql\query ) else (Set server =%%b)
)
echo Server name "%server%"
sqlcmd -s %server%
value of %%a is not assigned to variable Word1. But when i echo the %%a, it shows the correct value. As well in the last empty value check if condition, the server variable never set. I am very confused here. can someone help me out??
P.S: input to the script is any 2 word string (ex: a.bat "l dev-server")
You need to use delayed expansion - !Word1! instead of %Word1%.
By default, when the shell first reads the statement, all variables are substituted with their current values, and the modified statement is used every time that line is hit. This is simply how DOS works, and it's kept that way in the Windows shell for backwards compatibility.
Delayed expansion, on the other hand, reinserts the value every time the statement is hit. That will give you the desired result.
Related
Is there anyway to do like this?
#echo off
ECHO WHAT do you want to do?
ECHO 1.MOVE
ECHO 2.COPY
set "Act="
IF %Act% EQU 1 SET process=MOVE ELSE SET process=COPY
%process% testfile.txt New
pause
#echo off
SETLOCAL
ECHO WHAT do you want to do?
ECHO 1.MOVE
ECHO 2.COPY
set /p "Act="
IF "%Act%" EQU "1" (SET "process=MOVE") ELSE (SET "process=COPY")
%process% testfile.txt New
pause
The setlocal makes sure that the environment changes made in the program (like setting act and process are discarded when the process ends, so the environment does not become contaminated with stale data.
the /p option to set means accept input implicitly from the keyboard but may also be from a file if redirected.
The quotes in the if ensure that each side of the comparison operator is non-null and interpreted as a single string if it contains separators like spaces.
The parentheses in the if are required (at least about the if-true part) in order that the else is not seen as a parameter to the potential executable in the if-true part.
The quotes in the plain set statements ensure that trailing spaces are not included in the value assigned (probably not actually required here, but it's a good idea to get used to doing that...you only need to be caught out once to waste a number of hours chasing stray spaces...)
I have a batch file that is scanning a file URLs.txt and for each url run it and download the file. The issue I have is the environment variable within the FOR loop. I am using cat, sed and awk to get the last two parts of the the url so I can provide the filename. The issue is the environment variable is never updated after the first run. I can see that tmp2.txt just updated correctly for every url, but the batch file is not updating outfile and thus I keep overwriting the first file.
I tried to simplify the batch file for a test and any variable within a for loop never seems to update.
#echo off
setlocal enabledelayedexpansion
for /F "tokens=*" %%A in (URLs.txt) do (
echo %%A > tmp.txt
cat tmp.txt | sed "s/\(.*\)\//\1_/" | awk -F "/" "{print $NF}" > tmp2.txt
set /p outfile=<tmp2.txt
echo Varible A
echo %%A
echo.
echo Varible outfile
echo %outfile%
call curl.exe -o %outfile% -u username:password --insecure %%A
pause
)
Why is environment variable outfile not updated within FOR loop?
echo !outfile!
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.
In your case, since outfile is assigned the value %%A, you can replace %outfile% with %%A - and it would be an idea to "quote the string" anyway since "quoting a string" makes it a single token rather than a series - just in case your filenames contain separators like Space
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 have a problem removing trailing \ in a script, my current script is:
echo on
setlocal enableextensions enabledelayedexpansion
SET SCRIPTFOLDER=C:\install$
FOR /F "tokens=* delims=," %%a in (%SCRIPTFOLDER%\GetFilesandFoldersFromHere.Txt) DO (
set data.path=%%~pa
SET data.path=%data.path:~0,-1%
echo %data.path%
rem echo file and folder= %%~na%%~xa Folder=%data.path%
)
The GetFilesandFoldersFromHere.Txt file has lines of files and location e.g.:
T:\First File Here\Move this File.txt
When I run the above code I get:
C:\install$\file Archive Scripts>(
set data.path=\First File Here\
rem If ~-1data.path:~0,0
SET data.path=~0,-1
echo
rem echo file and folder= Move this File.txt Folder=
I want to assign data.path the directory (without drive letter and the trailing ). It assigns the value but when I try to get rid of the trailing \ the value is nulled.
Does anyone have an idea whta is wrong with the code? I am sure it is a simple solution. Been banging my head against this screen, can't see the woods from the tree at the moment.
The problem is that when the for code block (the code enclosed in parenthesis) is parsed, all the variable read operations are replaced with the value in the variable before starting to execute, and in each iteration what is used is this initial value, and not the value stored into the variable during the execution.
If you change a variable inside a block of code and need to access the changed value inside the same block of code, you need to enable delayed expansion (setlocal enabledelayedexpansion) and change (where necessary) the syntax to access the variables to use !varName! instead of %varName%. This indicates to the parser that this read operation must be delayed.
So, in your code you have delayed expansion enabled, but
SET data.path=%data.path:~0,-1%
echo %data.path%
should be something like
SET data.path=!data.path:~0,-1!
echo !data.path!
Here's a trick to remove the trailing backslash
#echo off
set "folder=c:\data\"
for %%a in ("%folder%\.") do set "folder=%%~dpnxa"
set fold
pause
My batch file terminates prematurely after I assign the first environmental variable (script output below). I've tried turning echo on, using errorlevels, sending the output to a text file, and checking syntax. I've spent several hours researching debugging batch scripts, but I have finally hit a brick wall.
Script's Goal: Search each directory name of the user's Program Files, looking for common antivirus programs. I realize that it would be easiest iterate through an array of antivirus names for this purpose, but I want to keep it simple for now.
#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 echo %ERRORLEVEL%
else echo "env. variable created successfully."
for /d %%f in (""%ProgramFiles%\*"") do (
{
IF NOT ERRORLEVEL 0 echo %ERRORLEVEL%
echo "%%f"
if exist /i "*McAfee*" < %%f %AntiVirus1%="McAfee"
::find "Norton" < %%f
::find "Comodo" < %%f
::find "AVG" < %%f
}
echo %AntiVirus1%
#pause
Output of this script:
C:\Users\Matt\Desktop>set AntiVirus1="Initial Value"
C:\Users\Matt\Desktop>
Can someone point me to what I'm doing wrong?
UPDATE Corrected script, now working but returning incorrect results:
::#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 (echo %ERRORLEVEL%) ELSE echo "env. variable created successfully."
echo Checking Program Files...
for /d %%f in ("%ProgramFiles%\*") do (
echo "%%f"
if %%f=="*adobe*" set AntiVirus1="adobe"
)
echo %AntiVirus1% found
#pause
First of all, ELSE must be on the same line with IF or on the same line with the closing parenthesis that pertains to IF. In you particular case you should change your first IF...ELSE command like this:
IF NOT ERRORLEVEL 0 (ECHO %ERRORLEVEL%) ELSE ECHO "env. variable created successfully."
or like this:
IF NOT ERRORLEVEL 0 (
ECHO %ERRORLEVEL%
) ELSE ECHO "env. variable created successfully."
(Capitalisation and indentation are perfectly optional.)
Other issues:
Duplicated quotation marks in the FOR loop header:
for /d %%f in (""%ProgramFiles%\*"") do (
should be
for /d %%f in ("%ProgramFiles%\*") do (
Braces ({, }) around the loop body. They are not part of the loop syntax (in fact, they are not part of batch scripting syntax at all), so should be dropped.
No closing parenthesis matching the opening one after DO. It should be added on a separate line after the loop body.
Incorrect use of ::-style comments in the loop body. They are not allowed inside bracketed blocks. Use REM instead.
UPDATE
In batch scripting, testing for a substring is done somewhat unusually. You'll need another environment variable and you'll also need to enable delayed expansion. The latter is not really connected with the comparison, but it is needed because the comparison is going to be performed within a bracketed block.
Here's your new script modified, with the changes highlighted:
::#echo off
::variables
set AntiVirus1="Initial Value"
IF NOT ERRORLEVEL 0 (echo %ERRORLEVEL%) ELSE echo "env. variable created successfully."
SETLOCAL EnableDelayedExpansion
echo Checking Program Files...
for /d %%f in ("%ProgramFiles%\*") do (
echo "%%f"
SET "folder=%%f"
if /I NOT "!folder:adobe=!"=="!folder!" set AntiVirus1="adobe"
)
echo %AntiVirus1% found
#pause
Here's a bit of explanation.
The ! syntax is a delayed expansion equivalent of % and is used with environment variables only, not with loop variables and not with command line parameters. Delayed expansion is needed because we are in a bracketed block. A bracketed block is parsed entirely before it starts executing, so all %var% expressions are expanded (evaluated) before the block starts and are not changed throughout the block's execution. That cannot suit us because we need to assign different values to a variable during the block's execution, and the values must be read within the block. Delayed expansion, as follows from the name, delays the expansion of a variable until the actual execution of every single command that references that variable. Because immediate expansion can still be used alongside delayed expansion, a different syntax is introduced, which is ! around variable names, instead of %.
!folder:adobe=! means evaluate folder replacing every occurrence of adobe with an empty string. The result of this expression is then compared to the (unchanged) value of folder. If there's a match, then the replacement didn't occur, which means there was no adobe in the value of folder in the first place. In this case we should do nothing. But if there was not a match, i.e. if the modified value didn't match the unmodified one, then we should set the AntiVirus1 variable. This is why there's NOT in front of the comparison.
The /I option simply means case-insensitive comparison.