I'm trying to accomplish the following ridiculous task:
I have a text file containing a set of fully qualified filesnames. I want to iterate through the file and append each line to a common variable, that can be passed to a command line tool. For example, the file might be:
C:\dir\test.txt
C:\WINDOWS\test2.txt
C:\text3.txt
and I'd like to assign them to some variable 'a' such that:
a = "C:\dir\test.txt C:\WINDOWS\test2.txt C:\text2.txt"
A secondary question is - what is a good batch file reference? I'm finding some stuff in the Windows material, and a lot of home-grown websites, but nothing particularly complete.
As for references, SS64.com isn't bad. Rob van der Woude gets linked fairly often, too.
As for your problem, that's easy:
#echo off
setlocal enableextensions enabledelayedexpansion
set LIST=
for /f %%x in (yourfile.txt) do (
set LIST=!LIST! "%%x"
)
echo %LIST%
endlocal
More in-depth explanation:
setlocal enableextensions enabledelayedexpansion
We're enabling delayed expansion here. This is crucial as otherwise we wouldn't be able to manipulate the list of files within the for loop that follows.
for /f %%x in (yourfile.txt) do (
set LIST=!LIST! "%%x"
)
for /f iterates over lines in a file, so exactly what we need here. In each loop iteration we append the next line to the LIST variable. Note the use of !LIST! instead of the usual %LIST%. This signals delayed expansion and ensures that the variable gets re-evaluated every time this command is run.
Usually cmd expands variables to their values as soon as a line is read and parsed. For cmd a single line is either a line or everything that counts as a line, which happens to hold true for blocks delimited by parentheses like the one we used here. So for cmd the complete block is a single statement which gets read and parsed once, regardless of how often the interior of the loop runs.
If we would have used %LIST% here instead of !LIST! then the variable would have been replaced immediately by its value (empty at that point) and the loop would have looked like this:
for /f %%x in (yourfile.txt) do (
set LIST= "%%x"
)
Clearly this isn't what we wanted. Delayed expansion makes sure that a variable is expanded only when its value is really needed. In this case when the interior of the loop runs and constructs a list of file names.
Afterwards the variable %LIST% or !LIST! (now it doesn't really matter anymore which to use) contains the list of lines from the file.
Funnily enough, the help for the set command includes exactly this example for delayed expansion:
Finally, support for delayed
environment variable expansion has
been added. This support is always
disabled by default, but may be
enabled/disabled via the /V command
line switch to CMD.EXE. See CMD /?
Delayed environment variable expansion
is useful for getting around the
limitations of the current expansion
which happens when a line of text is
read, not when it is executed. The
following example demonstrates the
problem with immediate variable
expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since
the %VAR% in BOTH IF statements is
substituted when the first IF
statement is read, since it logically
includes the body of the IF, which is
a compound statement. So the IF
inside the compound statement is
really comparing "before" with "after"
which will never be equal. Similarly,
the following example will not work as
expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of
files in the current directory, but
instead will just set the LIST
variable to the last file found.
Again, this is because the %LIST% is
expanded just once when the FOR
statement is read, and at that time
the LIST variable is empty. So the
actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the
last file found.
Delayed environment variable expansion
allows you to use a different
character (the exclamation mark) to
expand environment variables at
execution time. If delayed variable
expansion is enabled, the above
examples could be written as follows
to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
What you're after can be done with a FOR /F command.
Here's a good resource I've used many times:
http://www.robvanderwoude.com/batchfiles.php
A good book: Windows NT Shell Scripting by Tim Hill. The edition I have was published in 1998 but it is still valid for Windows command programs in Windows 2008.
type *commonfilepart* >> concat_result_file
OS: WINDOWS SERVER 2003
Related
I have the following snippet (some code omitted):
REM Verify destination_folders.
FOR %%f in (%destination_folders%) do (
IF NOT EXIST %%f (
echo(
echo Destination folder %%f does not exist. Is version %version% correct? Please update the script with your required parameters.
SET /p continue="Do you want to abort the operation? [Y/N]:"
echo cont=%continue%
IF %continue:~0,1%==y (
goto :eof
)
IF %continue:~0,1%==Y (
goto :eof
)
)
)
The issue is that the variable continue is not being assigned until after the script has been executed. For example, if continue was N from the the previous execution and then I input Y it will not update it, hence the echo after the assignment will output cont=N although it should be cont=Y. What am I doing wrong?
You need delayed expansion. setlocal enabledelayedexpansion somewhere, and use !continue! instead of %continue%. The problem is that %continue% is expanded as the entire parenthetical code block is read for the first time, so it's treated as flat text by the time for performs its first loop. !continue! retains its variability, and it's re-evaluated on each iteration.
You can also avoid the delayed expansion problem by using choice and if errorlevel like this:
REM Verify destination_folders.
FOR %%f in (%destination_folders%) do (
IF NOT EXIST "%%~f" (
echo(
echo Destination folder %%f does not exist. Is version %version% correct?
echo Please update the script with your required parameters.
choice /N /M "Do you want to abort the operation? [Y/N]: "
if not errorlevel 2 goto :EOF
)
)
To learn in greater detail about delayed expansion, read this portion of the help set documentation:
Finally, support for delayed environment variable expansion has been added. This support is always disabled by default, but may be enabled/disabled via the /V command line switch to CMD.EXE. See CMD /?
Delayed environment variable expansion is useful for getting around the limitations of the current expansion which happens when a line of text is read, not when it is executed. The following example demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements is substituted when the first IF statement is read, since it logically includes the body of the IF, which is a compound statement. So the IF inside the compound statement is really comparing "before" with "after" which will never be equal. Similarly, the following example will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory, but instead will just set the LIST variable to the last file found. Again, this is because the %LIST% is expanded just once when the FOR statement is read, and at that time the LIST variable is empty. So the actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different character (the exclamation mark) to expand environment variables at execution time. If delayed variable expansion is enabled, the above examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
#echo off
cd %~dp0
md .\newfolder
for /f "usebackq delims=" %%f in ("list.txt") do (
call set /a add=%%add%%+1
call set addx=0000%%add%%
call set addx=%%addx:~-3%%
call copy "%%f" ".\newfolder\%%addx%%_%%f"
)
pause
I made simple namechange code. I usually use command without 'call' but here it makes error message . why is that? .. and when i use %variable% not %%variable%% , It doesn't work well..
plz tell me why it happens.. and last question.. environment variable's value is stored until exit cmd . I want to know how i can unset that.. thank you..
All code within a parenthesized block is parsed in one pass. Normal variable expansion using percents occurs at parse time. So if you set a variable within a block, you cannot access the value using normal expansion because the value will be the value that existed before you entered the block.
You have the above situation. There are two classic ways to resolve the problem.
1) You can use CALL and double the percents as you have done. The CALL solves the problem because normal expansion occurs twice for a called line - once for the entire block, and again before the line is executed, but after previous lines in the block have executed. The first expansion converts the double percents to single percents, and the second expansion actually expands the variable.
I do not like this solution because it is slow, and also because the CALL causes problems with quoted ^ characters - they are doubled.
You can use multiple CALLs on the same command. Each Call requires the percents to be doubled. So one CALL requires 2 percents, two CALLs requires 4 perecents, three CALLs 8 percents, etc.
2) I think the preferred solution is to use delayed expansion. It is much faster, and also you never have to worry about escaping or quoting special characters like &, |, >, < etc. when you used delayed expansion. Delayed expansion does just what it says - the variable is not expanded until just before the line is executed. Delayed expansion must be enabled before it can be used. Within a batch file you can use setlocal enableDelayedExpansion.
The one problem that can occur with delayed expansion is FOR variables are corrupted if they contain ! and delayed expansion is enabled when they are expanded. That can usually be solved by toggling delayed expansion on and off within the loop.
If you type HELP SET from the command prompt, you will get a pretty good description of the problem with expanding variables within a block of code, and how delayed expansion can help. The description starts about half way down with the words Finally, support for delayed environment variable expansion....
Note - you do not need to expand variables when used within a SET /A computation. SET /A will automatically expand the value at execution time. Undefined variables are treated as zero.
In your code, you can simply use set /a add=add+1
But there is an even simpler shorthand way - you can use the += operator: set /a add+=1.
Here is another way your code could be written without using CALL. The code is untested, but I think I got it right.
#echo off
setlocal disableDelayedExpansion
cd "%~dp0"
md newfolder
set add=0
for /f "usebackq eol=: delims=" %%F in ("list.txt") do (
set /a add+=1
set "file=%%F"
setlocal enableDelayedExpansion
set "addx=00!add!"
copy "!file!" "newfolder\!addx:~-3!_!file!"
endlocal
)
pause
I explicitly initialize add to 0 because it might already be set to a value. If you know that it is undefined or already set to 0, then the initialization is not needed.
Your FOR loop is dealing with file names, and ! is valid within file names. That is the reason I toggle delayed expansion on and off within the loop - I don't want file names with ! to be corrupted when I expand %%F. File names can also start with ; (though highly unlikely). If it does, then FOR will skip that file because the default EOL character is ;. A file can never start with :, so I like to set EOL to : instead.
I put SETLOCAL near the top so that the environment variable definitions do not persist after the batch file completes.
I'd like to put each of the many properties' file names into variable fileName and echo them out to the command prompt window. But only the last properties file name to be cycled thru is printed out as many times as there are properties files. Is there an easy fix to this problem. I know that ...DO echo %%-nxG can do the same thing but I'd like to save the file name in %%~nxG for future use.
FOR %%G IN (C:\ExecutionSDKTest_10.2.2\*.properties) DO (
set fileName=%%~nxG
echo %fileName%
)
You need to use delayed expansion:
setlocal enabledelayedexpansion
FOR %%G IN (C:\ExecutionSDKTest_10.2.2\*.properties) DO (
set fileName=%%~nxG
echo !fileName!
)
Environment variables in cmd are expanded when a command is parsed – in this case this includes the whole block in parentheses. So %fileName% gets replaced by an empty string because it didn't have a value before the loop ran. Delayed expansion uses ! instead of % and changes variable evaluation so that they are evaluated just before a command is run.
help set has more details about why and when it is necessary. In general, whenever you modify and use a variable within a loop you have to use delayed expansion, but it comes with a few other benefits too.
I notice in most scripts, the two are usually in the same line as so:
SETLOCAL ENABLEDELAYEDEXPANSION
Are the two in fact separate commands and can be written on separate lines?
Will setting ENABLEDELAYEDEXPANSION have an adverse effect on a script if it is set on the first lines of the script and not disabled until the end of the script?
I think you should understand what delayed expansion is. The existing answers don't explain it (sufficiently) IMHO.
Typing SET /? explains the thing reasonably well:
Delayed environment variable expansion is useful for getting around
the limitations of the current expansion which happens when a line of
text is read, not when it is executed. The following example
demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements
is substituted when the first IF statement is read, since it logically
includes the body of the IF, which is a compound statement. So the IF
inside the compound statement is really comparing "before" with
"after" which will never be equal. Similarly, the following example
will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory,
but instead will just set the LIST variable to the last file found.
Again, this is because the %LIST% is expanded just once when the FOR
statement is read, and at that time the LIST variable is empty. So the
actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
Another example is this batch file:
#echo off
setlocal enabledelayedexpansion
set b=z1
for %%a in (x1 y1) do (
set b=%%a
echo !b:1=2!
)
This prints x2 and y2: every 1 gets replaced by a 2.
Without setlocal enabledelayedexpansion, exclamation marks are just that, so it will echo !b:1=2! twice.
Because normal environment variables are expanded when a (block) statement is read, expanding %b:1=2% uses the value b has before the loop: z2 (but y2 when not set).
ENABLEDELAYEDEXPANSION is a parameter passed to the SETLOCAL command (look at setlocal /?)
Its effect lives for the duration of the script, or an ENDLOCAL:
When the end of a batch script is reached, an implied ENDLOCAL is
executed for any outstanding SETLOCAL commands issued by that batch
script.
In particular, this means that if you use SETLOCAL ENABLEDELAYEDEXPANSION in a script, any environment variable changes are lost at the end of it unless you take special measures.
The ENABLEDELAYEDEXPANSION part is REQUIRED in certain programs that use delayed expansion, that is, that takes the value of variables that were modified inside IF or FOR commands by enclosing their names in exclamation-marks.
If you enable this expansion in a script that does not require it, the script behaves different only if it contains names enclosed in exclamation-marks !LIKE! !THESE!. Usually the name is just erased, but if a variable with the same name exist by chance, then the result is unpredictable and depends on the value of such variable and the place where it appears.
The SETLOCAL part is REQUIRED in just a few specialized (recursive) programs, but is commonly used when you want to be sure to not modify any existent variable with the same name by chance or if you want to automatically delete all the variables used in your program. However, because there is not a separate command to enable the delayed expansion, programs that require this must also include the SETLOCAL part.
A real problem often exists because any variables set inside will not be exported when that batch file finishes. So its not possible to export, which caused us issues. As a result, I just set the registry to ALWAYS used delayed expansion (I don't know why it's not the default, could be speed or legacy compatibility issue.)
File test.cmd:
name=dummy
for /f "eol=; tokens=1 delims=," %%i in (list.txt) do (
echo i: %%i
set name=%%i
echo name: %name%)
the file list.txt contains this lines (one name per line):
John
Tom
Erica
Sara
Each time I launch this batch I get this output:
i: John
name: dummy
i:T om
name: dummy
i: Erica
name: dummy
i: Sara
name: dummy
It seems that the variable name does not get assigned the value of %%i
Any idea?
You're falling into the old trap of not using delayed expansion.
For a quick fix, just put
setlocal enabledelayedexpansion
before that loop in your batch file and use !name! instead of %name%.
CMD expands variables while parsing a command. A command in this sense is a single line or a “block”, delimited with parentheses. The complete for loop is only parsed once and in that stage %name% gets replaced with the value it has at that point, namely "dummy". Delayed expansion on the other hand uses ! instead of % to delimit variable names and the variables then get expanded right before execution of a command.
Whenever you are setting a variable inside a parenthesized block and use its value in the same block again you need to use delayed expansion. help set has also some info about this:
Delayed environment variable expansion
is useful for getting around the
limitations of the current expansion
which happens when a line of text is
read, not when it is executed. The
following example demonstrates the
problem with immediate variable
expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since
the %VAR% in both IF statements is
substituted when the first IF
statement is read, since it logically
includes the body of the IF, which is
a compound statement. So the IF
inside the compound statement is
really comparing “before” with “after”
which will never be equal. Similarly,
the following example will not work as
expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will not build up a list of
files in the current directory, but
instead will just set the LIST
variable to the last file found.
Again, this is because the %LIST% is
expanded just once when the FOR
statement is read, and at that time
the LIST variable is empty. So the
actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the
last file found.
Delayed environment variable expansion
allows you to use a different
character (the exclamation mark) to
expand environment variables at
execution time. If delayed variable
expansion is enabled, the above
examples could be written as follows
to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%