#echo off
setlocal enabledelayedexpansion
::just a sample adapter here:
set "adapter=Ethernet adapter Local Area Connection"
set adapterfound=false
echo Network Connection Test
for /f "usebackq tokens=1-2 delims=:" %%f in (`ipconfig /all`) do
(
set "item=%%f"
if /i "!item!"=="!adapter!"
(
set adapterfound=true
)
else if not "!item!"=="!item:IP Address=!" if "!adapterfound!"=="true"
(
echo Your IP Address is: %%g
set adapterfound=false
)
)
VERY new to batch, can someone explain a few things here:
What does setlocal enabledelayedexpansion do in this particular instance?
What does usebackq do? (I've tried looking this up but didn't quite understand)
How did the variable %%g get initialized and is it global or local?
Thank you for your time!
Delayed Expansion will cause variables to be expanded at execution time rather than at parse time.
Code below would seem to echo second second, but it prints first second instead.
setlocal EnableDelayedExpansion
set var=first
set var=second & Echo %var% !var!
Source: SS64
usebackq forces for loops use backquotes (`dir`) to evaluate the commands inside it and use the output for the forloop, instead of open a file. This will list all elements of Documents predeced with an asterisk.
echo Documents:
for /f "usebackq" %%i in (`dir /b "C:\Users\%username%\Documents\"`) do (
echo * %%i
)
pause
They're local. Using the previous example, %%iis defined just in the for loop, no additional initialization needed.
Related
My goal is to load a text file into a global array, and the file contains exclamation marks. I want to then bubble sort the array, and save the contents to a new file. Unfortunately I cannot even get past loading data into an array. I tried using for loops, but it seems delayed expansion is both needed and not needed. Of course, any variables made inside the loops are local variables due to the setlocal command, correct? So once endlocal is reached (in order to toggle delayed expansion), the scope is gone. I was thinking that I need the array to be global so that I can continue manipulating it later on. (I tried to add set commands at the end of endlocal to pass the local values up to higher scope, but my attempts got me nowhere.)
setlocal enabledelayedexpansion
set /a count=0
set "file=list.txt"
echo Extracting titles and IDs...
for /f "usebackq delims=; tokens=1,2* skip=1" %%a in ("%file%") do (
set /a count+=1
setlocal disabledelayedexpansion
set "title=%%b"
set "gameid=%%a"
setlocal enabledelayedexpansion
for /f "delims=- tokens=1,2*" %%x in ("!gameid!") do (
echo %%y %%x "!title!"
set "entry[!count!]=%%y %%x !title!"
echo %count% - !count!
)
endlocal
endlocal
)
pause
The input file looks like:
ID;Title;Other;Headings;We;Dont;Need
ABC-456;My second game ever! Even better!;foo;bar;blah;blah;blah
XYZ-123;My first game ever! Yes!;foo;bar;blah;blah;blah
My end goal is to create a file in the form
123 XYZ "My first game ever! Yes!"
456 ABC "My second game ever! Even better!"
after sorting by the numerical values. If anyone could help just get through the first part (loading data into an array), that would be greatly appreciated.
You can avoid delayed expansion completely by not using variables, when you can use for variables.
The only spot that would require delayed expansion, can be done by an alternative:
call set "entry[%%count%%]=%%Y %%X %%B"
call forces another layer of expansion. for that to work properly, you have to make sure, %%var%% doesn't get confused with %%v and ar%%. That's the reason, I changed the for variable to uppercase (%%a is different from %%A).
#echo off
setlocal disabledelayedexpansion
set /a count=0
set "file=list.txt"
echo Extracting titles and IDs...
for /f "usebackq delims=; tokens=1,2* skip=1" %%A in ("%file%") do (
set /a count+=1
for /f "delims=- tokens=1,2*" %%X in ("%%A") do (
call set "entry[%%count%%]=%%Y %%X %%B"
)
)
(for /f "tokens=1,* delims==" %%A in ('set entry[') do #echo %%B)|sort
pause
(Note: the sorting works only, when all the items have a three-digit number)
Output with your example data:
Extracting titles and IDs...
123 XYZ My first game ever! Yes!
456 ABC My second game ever! Even better!
What I want to achieve is have an output file that lists how many files are in a specific folder on a number of different remote machines ie(127.0.0.1 394files).
The issue I'm having is the output file generated only has 1 line in it, the last IP in ip.txt
To try and explain the code below, I'm creating a variable called 'testip' which should match the ip/machine I'm calling from ip.txt , this is created purely for the echo at the end, when I tried calling %%i or %%b at an echo, all that appears in the text file is %i or %b , as if its truncating 1 of the % characters. creating a separate variable (SET testip=%%b) got around that issue.
Next loop, it looks for the IP list in the ip.txt file and performs a count on the \test_scripts\ folder.
Then it should echo the IP it ran as well as the count number.
Any idea where this is going wrong?
Any feedback appreciated.
#ECHO OFF
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "delims=" %%b in (' type "C:\Users\testuser\Desktop\test_scripts\ip.txt" ' ) DO SET testip=%%b
for /f "delims=" %%i in (' type "C:\Users\testuser\Desktop\test_scripts\ip.txt" ' ) DO (
SET count=0
for %%o IN ('type "\\%%i\c$\Users\testuser\Desktop\test_scripts\*.*" ' ) DO (
SET /A count=count + 1
)
)
echo %testip% %count% >> output.txt
ENDLOCAL ENABLEDELAYEDEXPANSION
ENDLOCAL
Try this one instead. Note how delayed expansion is used. For more on the commands, run them with /? switch on cmdline. for instance. for /?
Also you will see that command type is not used at all.
#echo off
setlocal enabledelayedexpansion
set "location=C:\Users\testuser\Desktop\test_scripts\ip.txt"
for /f "delims=" %%i in (%location%) DO (
set "remote=\\%%i\c$\Users\testuser\Desktop\test_scripts\*"
set count=0
for %%x in (!remote!) do set /a count+=1
echo !remote! !count! >> output.txt
)
This is the same method as Gerhards answer but with two modifications. I've set the remote variable at the start of the script instead of repeating that on each iteration of the loop; and I'm outputting the IP address, instead of the remote path, with the file count.
#Echo Off
SetLocal EnableDelayedExpansion
Set "location=%UserProfile%\Desktop\test_scripts\ip.txt"
Set "remote=\\%%A\C$\Users\testuser\Desktop\test_scripts"
( For /F "Usebackq Delims=" %%A In ("%location%") Do (Set "#=0"
For %%B In ("%remote%\*") Do Set /A #+=1
Echo %%A !#!))>"output.txt"
This is similar except that it uses the Dir command to retrieve the file count. The Dir command can take account of hidden and system files etc.
#Echo Off
SetLocal EnableDelayedExpansion
Set "location=%UserProfile%\Desktop\test_scripts\ip.txt"
Set "remote=\\%%A\C$\Users\testuser\Desktop\test_scripts"
( For /F "Usebackq Delims=" %%A In ("%location%") Do (Set "_=" & Set "#="
For /F %%B In ('Dir "%remote%" /A-D/D') Do Set "#=!_!" & Set "_=%%B"
Echo %%A !#!))>"output.txt"
You can easily modify your location and remote paths on lines 3 and 4, ensuring that the \\%%A\ section remains unaltered.
In have not tested either script, nor have I made any attempt at error trapping etc. I'll leave that to you, should it be required.
I have this batch file
SETLOCAL ENABLEDELAYEDEXPANSION
SET count=1
FOR /F "tokens=* USEBACKQ" %%F IN (`find "# STATISTICS ON CLIENT_JE_AMT EY_ENT_DATE EY_EFF_DATE" "z_B10_VALIDATION.LOG"`) DO (
SET var!count!=%%F
SET /a count=!count!+1
)
ECHO %var1%
ECHO %var2%
for /f "tokens=1 delims=:" %%i in ("%var2%") do (
set stat_line_start=%%i
)
echo %stat_line_start%
set /a stat_line_end=!stat_line_start!+3
for /L %%j in (%stat_line_start%,1,%stat_line_end%) do (
echo %%j
set k=%%j
set cfirst=%k:~0,1%
set csec=%k:~1,1%
set cthird=%k:~2,1%
findstr /B "[%cfirst%-%cfirst%][%csec%-%csec%][%cthird%-%cthird%]:" "z_B10_VALIDATION.LOG">> z_STATS.txt
)
ENDLOCAL
when i run this, I am getting the correct value for variable "k" but having error on "cfirst", "csec" and "cthird" variable.
sample result line :
set k=254
set cfirst=~0,1
instead of
set cfirst=2
I am new on this field and just starting to explore. Hope you can help me.
Thank you in advance.
CMD expands variables when the command is parsed, not when it is run.
Because code blocks (i.e. chunks of code suurrounded by parentheses - like multi-line if statements and for loops) are considered a single command by the interepreter, any variables that you set inside of the block are set to the default value: nothing.
You can delay this expansion to runtime with the command setlocal enabledelayedexpansion (which you already included at the top of your code) and using the !variable! syntax instead of the %variable% syntax.
Hi I don't have much experience in batch-programming and have a problem. I have a .bat script that reads a file with a list of paths and i want to get the filename of these paths. I use the script in cygwin.
My code in the Script:
for /F %%a in (error1.txt) do (
set value=%%a
FOR /F %%I IN ("%value%") DO SET MYPATHFILE=%%~nxI
)
When i run the Script %value% is empty.
Value of error1.txt:
a/b/c/d/TextIWant
you need delayed expansion or you can directly use %%a:
for /F %%a in (error1.txt) do (
FOR /F %%I IN ("%%a") DO SET MYPATHFILE=%%~nxI
)
or
setlocal enableDelayedExpansion
for /F %%a in (error1.txt) do (
set value=%%a
FOR /F %%I IN ("!value!") DO SET MYPATHFILE=%%~nxI
)
It looks like you will need Delayed Expansion.
The current problem is, that you want to use a variable in the same set of brackets where you changed the value in (the surrounding For-Loop).
Add setlocal EnableDelayedExpansion to your code at the top and change the %value% to !value!
You can test the problem yourself with this code:
#echo off
setlocal EnableDelayedExpansion
set foo=bar
For /L %%a (1,2,1) do (
set foo=foobar
echo.old value %foo%
echo.new value !foo!
)
I hope it helped :)
Greetings
geisterfurz007
I made this code
dir /B /S %RepToRead% > %FileName%
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
echo %%a is working fine but echo %z% returns "echo disabled".
I need to set a %z% because I want to split the variable like %z:~7%
Any ideas?
There are two methods to setting and using variables within for loops and parentheses scope.
setlocal enabledelayedexpansion see setlocal /? for help. This only works on XP/2000 or newer versions of Windows.
then use !variable! instead of %variable% inside the loop...
Create a batch function using batch goto labels :Label.
Example:
for /F "tokens=*" %%a in ('type %FileName%') do call :Foo %%a
goto End
:Foo
set z=%1
echo %z%
echo %1
goto :eof
:End
Batch functions are very useful mechanism.
You probably want SETLOCAL ENABLEDELAYEDEXPANSION. See https://devblogs.microsoft.com/oldnewthing/20060823-00/?p=29993 for details.
Basically: Normal %variables% are expanded right aftercmd.exe reads the command. In your case the "command" is the whole
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
loop. At that point z has no value yet, so echo %z% turns into echo. Then the loop is executed and z is set, but its value isn't used anymore.
SETLOCAL ENABLEDELAYEDEXPANSION enables an additional syntax, !variable!. This also expands variables but it only does so right before each (sub-)command is executed.
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
This gives you the current value of z each time the echo runs.
I struggeld for many hours on this.
This is my loop to register command line vars.
Example : Register.bat /param1:value1 /param2:value2
What is does, is loop all the commandline params,
and that set the variable with the proper name to the value.
After that, you can just use
set value=!param1!
set value2=!param2!
regardless the sequence the params are given. (so called named parameters).
Note the !<>!, instead of the %<>%.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%P IN (%*) DO (
call :processParam %%P
)
goto:End
:processParam [%1 - param]
#echo "processparam : %1"
FOR /F "tokens=1,2 delims=:" %%G IN ("%1") DO (
#echo a,b %%G %%H
set nameWithSlash=%%G
set name=!nameWithSlash:~1!
#echo n=!name!
set value=%%H
set !name!=!value!
)
goto :eof
:End
Simple example of batch code using %var%, !var!, and %%.
In this example code, focus here is that we want to capture a start time using the built in variable TIME (using time because it always changes automatically):
Code:
#echo off
setlocal enabledelayedexpansion
SET "SERVICES_LIST=MMS ARSM MMS2"
SET START=%TIME%
SET "LAST_SERVICE="
for %%A in (%SERVICES_LIST%) do (
SET START=!TIME!
CALL :SOME_FUNCTION %%A
SET "LAST_SERVICE=%%A"
ping -n 5 127.0.0.1 > NUL
SET OTHER=!START!
if !OTHER! EQU !START! (
echo !OTHER! is equal to !START! as expected
) ELSE (
echo NOTHING
)
)
ECHO Last service run was %LAST_SERVICE%
:: Function declared like this
:SOME_FUNCTION
echo Running: %1
EXIT /B 0
Comments on code:
Use enabledelayedexpansion
The first three SET lines are typical
uses of the SET command, use this most of the time.
The next line is a for loop, must use %%A for iteration, then %%B if a loop inside it
etc.. You can not use long variable names.
To access a changed variable such as the time variable, you must use !! or set with !! (have enableddelayexpansion enabled).
When looping in for loop each iteration is accessed as the %%A variable.
The code in the for loop is point out the various ways to set a variable. Looking at 'SET OTHER=!START!', if you were to change to SET OTHER=%START% you will see why !! is needed. (hint: you will see NOTHING) output.
In short !! is more likely needed inside of loops, %var% in general, %% always a for loop.
Further reading
Use the following links to determine why in more detail:
Difference between %variable% and !variable! in batch file
Variable usage in batch file
To expand on the answer I came here to get a better understanding so I wrote this that can explain it and helped me too.
It has the setlocal DisableDelayedExpansion in there so you can locally set this as you wish between the setlocal EnableDelayedExpansion and it.
#echo off
title %~nx0
for /f "tokens=*" %%A in ("Some Thing") do (
setlocal EnableDelayedExpansion
set z=%%A
echo !z! Echoing the assigned variable in setlocal scope.
echo %%A Echoing the variable in local scope.
setlocal DisableDelayedExpansion
echo !z! &rem !z! Neither of these now work, which makes sense.
echo %z% &rem ECHO is off. Neither of these now work, which makes sense.
echo %%A Echoing the variable in its local scope, will always work.
)
set list = a1-2019 a3-2018 a4-2017
setlocal enabledelayedexpansion
set backup=
set bb1=
for /d %%d in (%list%) do (
set td=%%d
set x=!td!
set y=!td!
set y=!y:~-4!
if !y! gtr !bb1! (
set bb1=!y!
set backup=!x!
)
)
rem: backup will be 2019
echo %backup%
Try this:
setlocal EnableDelayedExpansion
...
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
You can use a macro if you access a variable outside the scope
#echo off
::Define macro
set "sset=set"
for /l %%a in (1,1,4) do (
::set in loop
%sset% /a "x[%%a]=%%a*%%a"
if %%a equ 4 (
:: set in condition
%sset% "x[%%a]=x Condition"
%sset% "y=y Condition"
)
)
echo x1=%x[1]% x2=%x[2]% x3=%x[3]% x4=%x[4]% y=%y%
:: Bonus. enableDelayedExpansion used to access massive from the loop
setlocal enableDelayedExpansion
echo Echo from the loop
for /l %%a in (1,1,4) do (
::echo in one line - echo|set /p =
echo|set /p "=x%%a=!x[%%a]! "
if %%a equ 4 echo y=%y%
)
pause
I know this isn't what's asked but I benefited from this method, when trying to set a variable within a "loop". Uses an array. Alternative implementation option.
SETLOCAL ENABLEDELAYEDEXPANSION
...
set Services[0]=SERVICE1
set Services[1]=SERVICE2
set Services[2]=SERVICE3
set "i=0"
:ServicesLoop
if defined Services[%i%] (
set SERVICE=!Services[%i%]!
echo CurrentService: !SERVICE!
set /a "i+=1"
GOTO :ServicesLoop
)
The following should work:
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)