The problem is echo !out! show all lines but echo %data% which assigned from !out! only show first line
#echo off
call :f
echo %data%
:f
setlocal EnableDelayedExpansion
set out=
set NL=^
for /f "delims=" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo !out!
set data=!out!
exit /b 0
What is the correct way to pass the value entirely to other variable which i need to use freely after endlocal?
Thank you #aschipfl's for your answer that clarify that it's impossible to do it without end up in for-in loop again. Actually all that I want is to simplify my code without have to write for-in loop many times by turn it into a subroutine. I've solved it by passing the subroutine to the subroutine instead.
As derived from this thread, you cannot echo a multi-line string using immediate (%-)expansion, because everything after the first line-break is ignored.
To make your script working you need to correct two issues:
before the line endlocal & set data=%out% you must replace every new-line in variable out by an escaped new-line, that is the sequence ^ plus new-line plus new-line, which is exactly the same that you are using for defining the variable NL;
echo %data% truncates the displayed string at the first occurrence of a new-line in the value of variable data, so you need to use set data to show the actual content of the variable (or more precisely said, of all variables whose names begin with data);
Both of these items are commented (rem) in the following code:
#echo off
setlocal EnableDelayedExpansion
set out=
set NL=^
for /F "delims= eol=|" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo * Original variable content:
set out
rem // Replace every new-line by an escaped new-line:
set out=!out:^%NL%%NL%=^^^%NL%%NL%^%NL%%NL%!
echo * Modified variable content:
set out
endlocal & set data=%out%
rem // Do not use `echo` to show true content of variable:
echo * Returned variable content:
set data
echo * Mistaken variable content:
echo %data%
exit /B 0
Although the variable value is now correctly passed over the endlocal barrier, this approach is not exactly brilliant, because it does not allow you to use variable %data% (again because everything after the first line-break is ignored as initially mentioned), unless you have got delayed expansion enabled in the hosting cmd instance, which would permit to use !data!.
Another remaining problem is that special characters in the multi-line string (like ^, &, (, ) and ", <, >, |) may cause syntax errors or other unexpected issues. However, this can be avoided by using a for meta-variable rather than a normal environment variable for passing the variable value beyond the endlocal barrier, because the former are expanded after special character recognition, in contrast to the latter, which are expanded before:
#echo off
setlocal EnableDelayedExpansion
set out=
set NL=^
for /F "delims=" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo # Original variable content:
set out
rem /* Use a `for` meta-variable rather than a normal environment variable to
rem pass the variable value beyond the `endlocal` barrier;
rem a standard `for` loop can be used here, because there are not going to be
rem wildcards `?` and `*` in the variable value since they have already been
rem resolved by `dir`; `for /F` cannot be used here due to the new-lines: */
for %%j in ("!out!") do endlocal & set "data=%%~j"
rem // Do not use `echo` to show true content of variable:
echo # Returned variable content:
set data
echo # Mistaken variable content:
echo %data%
exit /B 0
The problem not being able to use variable %data% remains though.
To be able to use variable %data% with immediate expansion you could however simply store escaped new-lines rather than literal ones in the variable, because upon expansion you will have the intended literal new-line:
#echo off
setlocal EnableDelayedExpansion
set out=
set NL=^
for /F "delims=" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo # Original variable content:
set out
rem // Replace every new-line by an escaped new-line:
set out=!out:^%NL%%NL%=^^^%NL%%NL%^%NL%%NL%!
echo # Modified variable content:
set out
rem /* Use a `for` meta-variable rather than a normal environment variable to
rem pass the variable value beyond the `endlocal` barrier;
rem a standard `for` loop can be used here, because there are not going to be
rem wildcards `?` and `*` in the variable value since they have already been
rem resolved by `dir`; `for /F` cannot be used here due to the new-lines: */
for %%j in ("!out!") do endlocal & set "data=%%~j"
rem // Do not use `echo` to show true content of variable:
echo # Actual variable content:
set data
echo # Parsed variable content:
echo %data%
exit /B 0
But regard that this is only going to work when %data% does not appear within quoted ("") strings.
Related
I have a simple .bat with content:
#echo off
set "var=VAR1?VAR2?VAR3"
echo.%var%
echo.
for /F "delims=?" %%H in ('echo.%var%') do echo.%%~H
I would expect the following output:
VAR1?VAR2?VAR3
VAR1
VAR2
VAR3
Instead I get:
VAR1?VAR2?VAR3
VAR1
^ notice a trailing empty new line
Tried using different delimiter, usebackq, giving eol character, running through another for loop with different parameters, many other things; different variable name, different filename, setlocal w/o and w/ delayed expansion; no success.
If I include the tokens=1,2 parameter, it prints VAR1 into %%H and VAR2 into %%I as it should, but I need it to be "dynamic", give every element my %var% has.
EDIT: What is the most confusing to me is that if instead of writing my elements into a variable separated by a ?, I write it to a file, each element in its own line, then read the file line-by-line with "for /F "delims=*" %%H in ('type "VAR.txt"') do..."
it works perfeclty. Is it because the "delims=*", or is there a higher power messing with me? I don't see any difference from the FOR's perspective. If, by default, it should only give me the first token, why does it give every token from the file? Isn't token as new line the same as token as any other character?
You can also use this very simple trick that converts tokens into lines:
#echo off
setlocal EnableDelayedExpansion
set "var=VAR1?VAR2?VAR3"
echo %var%
echo/
for %%n in (^"^
%Do not remove this line%
^") do for /F "delims=" %%H in ("!var:?=%%~n!") do echo/%%~H
In this way, each token in the string: VAR1?VAR2?VAR3 is converted into separate lines like these ones:
VAR1
VAR2
VAR3
This is done replacing each ? character in the string by a NewLine (ASCII 10) character.
After that, you can process each line in any way you wish...
Each line read by a for/f is split into "tokens", substrings separated by any of the delimiters.
By default, tokens=1, so just the first token on the line is assigned to the metavariable %%H.
You could list each required token in a tokens= clause.
To assign each to %%H in turn, try
for %%H in (%var:?= %) do echo %%H
or if your elements contain spaces,
for %%H in ("%var:?=" "%") do echo.%%~H
The 'extra' newline is a red herring. You are misreading the result.
Here's a possible alternative methodology, which should be okay as long as each question mark delimited token does not contain a doublequote or exclamation mark character:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Dehine a variable named var.
Set "var=VAR 1?VAR,2?VAR|3?VAR;4?VAR<5?VAR>6?VAR&7?VAR=8"
Rem view the content of the variable named var.
Set /P "=%var%" 0<NUL & Echo(
Rem Display an optional empty line
Echo(
Rem Undefine and variables beginning with the string var[.
For /F "Delims==" %%G In ('"(Set var[) 2>NUL"') Do Set "%%G="
Rem define a increment counter
Set "i=1"
Rem Enable delayed variable expansion
SetLocal EnableDelayedExpansion
Rem Define variables named var[increment] splitting var at each question mark.
Set "var[%i%]=%var:?=" & Set /A i +=1 & Set "var[!i!]=%"
Rem Disable delayed variable expansion
Rem and display the individual incremented variable values.
For /F "Tokens=1-2,* Delims=[]=" %%G In ('"(Set var[) 2>NUL"') Do (EndLocal
Set /P "=%%I" 0<NUL & Echo()
Rem Display an optional empty line
Echo(
Pause
Please be aware that if there are more than nine tokens, the display order will not currently match the order in the initial variable. adjustments would have to be made in order to do that.
Expected output:
VAR 1?VAR,2?VAR|3?VAR;4?VAR<5?VAR>6?VAR&7?VAR=8
VAR 1
VAR,2
VAR|3
VAR;4
VAR<5
VAR>6
VAR&7
VAR=8
Press any key to continue . . .
Here's another possibility:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Define a variable named var.
Set "var=VAR 1?VAR,2?VAR;3?VAR=4?VAR!5?VAR&6?VAR)7?VAR^8?VAR#9"
Rem Display the content of the variable.
Set /P "=%var%" 0<NUL & Echo(
Rem Display an optional empty line.
Echo(
Rem Display the individual tokens.
For %%G In ("%var:?=","%") Do Set /P "=%%~G" 0<NUL & Echo(
Rem Display an optional empty line.
Echo(
Pause
This allows for an exclamation mark to exist in your file path tokens. It also has the additional benefit of being able to display the tokens in the same order, regardless of the number of them.
I should add that if you expect there to be percent characters in your tokens, you will have to perform an additional step, (due to those having a special meaning in cmd and batch files).
Expected output:
VAR 1?VAR,2?VAR;3?VAR=4?VAR!5?VAR&6?VAR)7?VAR^8?VAR#9
VAR 1
VAR,2
VAR;3
VAR=4
VAR!5
VAR&6
VAR)7
VAR^8
VAR#9
Press any key to continue . . .
In a complex batch file I want to read in files with paths, among other things, to read them into a variable one after the other separated by spaces.
This works with the following code so far quite well - but only if the path does not contain an exclamation mark.
Even using the setlocal command (enabledelayedexpansion / disabledelayedexpansion) I did not succeed in processing exclamation marks.
Does anyone here have a clever idea to the problem?
The following example batch creates a text file in the current directory and then reads it in a for /F loop.
At the end all three paths from the text file should be in the variable %Output%. But with the exclamation mark.
#echo off
setlocal enabledelayedexpansion
echo This is an example^^! > "textfile.txt"
echo This is a second example^^! >> "textfile.txt"
echo And this line have an ^^! exclamation mark in the middle >> "textfile.txt"
for /F "usebackq tokens=* delims=" %%a in (textfile.txt) do (
set "Record=%%a"
set "Output=!Output!!Record! - "
)
)
echo %Output%
echo !Output!
endlocal
The Output is like this:
This is an example - This is a second example - And this line have an exclamation mark in the middle
But should be like this:
This is an example! - This is a second example! - And this line have an ! exclamation mark in the middle
It is advisable not using delayed variable expansion on processing files and directories, lines in a text file, strings not defined by the batch file itself, or output captured from the execution of a program or a command line. If it is for some reasons necessary to make use of delayed variable expansion inside a FOR loop, there should be first assigned the file/directory name, the line, or the string to process to an environment variable while delayed expansion is disabled and then enable delayed expansion temporary inside the FOR loop.
Here is a batch file demo which can be simply run from within a command prompt window or by double clicking on the batch file. It creates several files for demonstration in the directory for temporary files, but deletes them all before exiting.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo This is an example!> "%TEMP%\TextFile.tmp"
echo This is a second example!>> "%TEMP%\TextFile.tmp"
echo And this line has an exclamation mark ! in the middle.>> "%TEMP%\TextFile.tmp"
set "Output="
(for /F usebackq^ delims^=^ eol^= %%I in ("%TEMP%\TextFile.tmp") do set "Line=%%I" & call :ConcatenateLines) & goto ContinueDemo
:ConcatenateLines
set "Output=%Output% - %Line%" & goto :EOF
:ContinueDemo
cls
echo/
echo All lines concatenated are:
echo/
echo %Output:~3%
set "Output="
del "%TEMP%\TextFile.tmp"
echo File with name ".Linux hidden file!">"%TEMP%\.Linux hidden file!"
echo File with name "A simple test!">"%TEMP%\A simple test!"
echo File with name " 100%% Development & 'Test' (!).tmp">"%TEMP%\ 100%% Development & 'Test(!)'.tmp"
echo/
echo Files with ! are:
echo/
for /F "eol=| tokens=* delims=" %%I in ('dir "%TEMP%\*!*" /A-D /B /ON 2^>nul') do (
set "NameFile=%%I"
set "FileName=%%~nI"
set "FileExtension=%%~xI"
set "FullName=%TEMP%\%%I"
setlocal EnableDelayedExpansion
if defined FileName (
if defined FileExtension (
echo File with ext. !FileExtension:~1!: !NameFile!
) else (
echo Extensionless file: !NameFile!
)
) else echo Extensionless file: !NameFile!
del "!FullName!"
endlocal
)
endlocal
echo/
#setlocal EnableExtensions EnableDelayedExpansion & for /F "tokens=1,2" %%G in ("!CMDCMDLINE!") do #endlocal & if /I "%%~nG" == "cmd" if /I "%%~H" == "/c" set /P "=Press any key to exit the demo . . . "<nul & pause >nul
The output of this batch file is:
All lines concatenated are:
This is an example! - This is a second example! - And this line has an exclamation mark ! in the middle.
Files with ! are:
File with ext. tmp: 100% Development & 'Test(!)'.tmp
Extensionless file: .Linux hidden file!
Extensionless file: A simple test!
The text file example with concatenating lines makes use of a subroutine called from within the FOR loop processing the lines in the text file. The syntax used here is for maximum performance by getting the subroutine as near as possible to the FOR command line. That is important if the FOR loop has to process hundreds or even thousands of items.
The example processing file names enables and disables delayed expansion inside the FOR loop after having assigned all parts of the currently processed file to environment variables. It could be useful to reduce the list of environment variables before processing thousands of files for a better performance on using this method.
Another method is shown in Magoo´s answer using the command CALL to get a command line with referenced environment variables (re)defined inside the loop parsed a second time. I used that method also in the past quite often, but don't that anymore as it is not fail-safe and not efficient. call set results in searching by cmd.exe in current directory and next in all directories of environment variable PATH for a file with name set and a file extension of environment variable PATHEXT. So it results in lots of file system accesses in the background on each iteration of the FOR loop and if there is by chance a file set.exe, set.bat, set.cmd, etc. found by cmd.exe somewhere, the batch file does not work anymore as expected because of running the executable or calling the batch file instead of the (re)definition of the environment variable.
The following answers written by me could be also helpful:
How to read and print contents of text file line by line?
It explains in full details how to process all lines of a text file.
How to pass environment variables as parameters by reference to another batch file?
It explains in full details what the commands SETLOCAL and ENDLOCAL do.
How to pass a command that may contain special characters (such as % or !) inside a variable to a for /f loop?
This is an example of a batch file designed to process video files with any valid file name on any Windows computer very efficient, safe and secure with full explanation.
Well, the main trick is to enable delayed expansion only when it is actually needed and to disable it otherwise. Since you are accumulating multiple strings in a single variable inside of a loop, it becomes a bit more difficult, because you should have delayed expansion disabled during expansion of for meta-variables (like %%a), but enabled when joining the string, leading to setlocal and endlocal statements inside of the loop. The major purpose of these commands is environment localisation, hence any variable changes become lost past endlocal, so a method of tansfering the value beyond endlocal is required, which is incorporated in the following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* At this point delayed expansion is disabled, so there is no need to
rem escape exclamation marks; moreover a redirected block prevents
rem superfluous file close/reopen operations, and there is no more
rem trailing space written to the file (as in your original approach): */
> "textfile.txt" (
echo This is an example!
echo This is a second example!
echo And this line have an ! exclamation mark in the middle
)
rem // Let us initialise the output variable:
set "Output= - "
rem // Using `usebackq` only makes sense when you want to quote a file path:
for /F "usebackq tokens=* delims=" %%a in ("textfile.txt") do (
rem // Remember that delayed expansion is still disabled at this point:
set "Record=%%a"
rem // For concatenation we need delayed expansion to be enabled:
setlocal EnableDelayedExpansion
set "Output=!Output!!Record! - "
rem /* We need to terminate the environment localisation of `setlocal`
rem inside of the loop, but we would lose any changes in `Output`;
rem therefore let us (mis-)use `for /F`, which is iterated once: */
for /F "delims=" %%b in ("!Output!") do endlocal & set "Output=%%b"
rem /* An often used method to transfer a variable beyond `endlocal` is
rem the line `endlocal & set "Output=%Output%`, but this only works
rem outside of a parenthesised block because of percent expansion. */
)
rem /* Echo out text with delayed expansion enabled is the only safe way;
rem surrounding separators ` - ` are going to be removed; since `Output`
rem was initialised with something non-empty, we do not even need to skip
rem sub-string expansion for the problematic case of an empty string: */
setlocal EnableDelayedExpansion
echo(!Output:~3,-3!
endlocal
endlocal
exit /B
Pew. I finally got it to work.
It works via a workaround using a second text file.
Not pretty, not performant, but it works and is sufficient for my purposes.
#Magoo, thanks for your post.
This is my solution:
#echo off
setlocal enableextensions enabledelayedexpansion
echo This is an example^^!> "textfile.txt"
echo This is a second example^^!>> "textfile.txt"
echo And this line have an ^^! exclamation mark in the middle>> "textfile.txt"
echo.
echo Content of the textfile:
type "textfile.txt"
set output=
del "textfile2.txt" 1> nul 2>&1
setlocal disabledelayedexpansion
for /f "usebackq tokens=* delims=" %%a IN ("textfile.txt") do (
rem Write each line without a newline character into a new text file
echo|set /p "dummy=%%a, ">>"textfile2.txt"
)
endlocal
rem Loading the content of the new text file into the variable
set /p output=<"textfile2.txt"
del "textfile2.txt" 1> nul 2>&1
echo.
echo --------------------------------------------
echo Content of the variable:
set out
endlocal
The output looks like this:
Content of the textfile:
This is an example!
This is a second example!
And this line have an ! exclamation mark in the middle
--------------------------------------------
Content of the variable:
output=This is an example!, This is a second example!, And this line have an ! exclamation mark in the middle,
It's delayedexpansion mode that appears to raise this problem.
#ECHO OFF
setlocal enabledelayedexpansion
echo This is an example^^^! > "textfile.txt"
echo This is a second example^^^! >> "textfile.txt"
echo And this line have an ^^^! exclamation mark in the middle >> "textfile.txt"
TYPE "textfile.txt"
SETLOCAL disabledelayedexpansion
for /F "usebackq tokens=* delims=" %%a in (textfile.txt) do (
set "Record=%%a"
CALL set "Output2=%%Output2%%%%record%% - "
CALL set "Output=%%Output%%%%a - "
SET out
)
)
endlocal&SET "output=%output%"
echo %Output%
echo !Output!
SET out
I've no doubt that with delayedexpansion off, there would be the same problem with %. Just special characters, I suppose.
Note that with endlocal&SET "output=%output%", the set is executed in delayedexpansion mode.
This question already has answers here:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
(8 answers)
Closed 3 years ago.
I ran across an issue using the SET command inside a FOR /F loop to echo the values of the loop parameters (e.g., %%g, %%h, etc.). I used the SET command because I wanted to see what values get assigned to each FOR /F loop parameter when the FOR /F loop iterates. I am specifically looking to see how many caret escape characters (^) are stripped by the CMD parser as it processes the line of test code that is the last element in the $code_test[00] array record:
#echo off
setlocal disabledelayedexpansion
set $code_test[00]=Record [00],2,green,blue,if not exist ^^!$m^^!^^!$n^^! (set /a $result+=3)
echo Value using ECHO command:
echo $code_test[00] = %$code_test[00]%
echo.
echo Value using SET command with Delayed Expansion DISABLED:
set $code_test[
echo.
setlocal enabledelayedexpansion
echo Value using SET command with Delayed Expansion ENABLED:
set $code_test[
echo.
for /f "tokens=1-10 delims=," %%g in ('set $code_test[') do (
echo For /f loop values using ECHO command ...
echo g = %%g
echo h = %%h
echo i = %%i
echo j = %%j
echo k = %%k
echo.
echo For /f loop values using SET command ...
set %%g
set %%h
set %%i
set %%j
set %%k
)
The code populates one record for the $code_test[xx] array with various elements, the last of which is a Windows CMD test code statement. I want to see the value of the FOR /F loop parameter that corresponds to this test code statement, which is %%k. So, I used both the echo and set commands to echo the value of %%k to see if %%k retained the caret (^) escape characters that were present in the original test code definition for $code_test[00]. The echo command worked as expected and stripped the caret escape characters (^) from the code. But the set command completely failed. I've read the post about how the CMD interpreter parses code here and I didn't see an answer to my question.
I realize that dealing with FOR /F loop parameters can be tricky. Should I just assume that I can't deal with FOR /F loop parameters directly (e.g., set %%k) and make it a policy to always assign the value of a FOR /F parameter to an environment variable then deal with the environment variable instead of the FOR /F parameter?
SET "blue62=108"
SET "%%%%k=hello"
ECHO after SET %%%%%%%% k errorlevel=!errorlevel!
set|FIND "k="
echo For /f loop values using SET command ...
ECHO ++++++++++++++++!errorlevel!
set %%g
set %%h
set %%i
set %%j
ECHO :::::::::::::::::::::!errorlevel!
SET if not exist !$m!!$n! (set /a $result+=3)
ECHO -----------!errorlevel!-------------------
ECHO :::::::::::::::::::::!errorlevel!
SET "if not exist !$m!!$n! (set /a $result+=3)"
ECHO -----------!errorlevel!-------------------
set %%k
)
I expanded your reporting to include the above code with these results:
For /f loop values using ECHO command ...
g = $code_test[00]=Record [00]
h = 2
i = green
j = blue
k = if not exist !$m!!$n! (set /a $result+=3)
after SET %%%% k errorlevel=0
For /f loop values using SET command ...
++++++++++++++++1
Environment variable 2 not defined
Environment variable green not defined
blue62=108
:::::::::::::::::::::1
-----------1-------------------
:::::::::::::::::::::1
-----------1-------------------
Environment variable %k not defined
I believe this demonstrates that set simply acts strangely if confronted with an argument that is not a simple space-absent string with optional = for assignment.
What was really curious was that the Environment variable %k not defined did not appear with the original code.
I believe you're really expecting a 1970's vintage design to not only be bullet-proof but act in a consistent manner when supplied with unanticipated data. Just treat it like your grandma. Very capable, but easily confused.
This code snippet below is stripped of all the extra junk, down to just the error-generating code,
)"" was unexpected at this time.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
Set "regex="(Test_Health=(?!100))""
echo Regex is: %regex%
FOR /L %%I IN (1,1,5) DO (
Set "to_call=call crv.exe "%%I" %regex%"
echo About to call: !to_call!
)
Basically, in the real script, I'm trying to call a command-line tool that takes a complex string with potentially special chars in it, as well as a regex.
I figured out a workaround, which was to add a single caret (^) before %%I's last quote:
Set "to_call=call crv.exe "%%I^" %regex%"
But that feels like a dirty hack. What am I doing wrong, and what should I do to get the desired behavior without a dirty hack?
To fix your problem without a hack:
Make sure that the ! char. in your regex variable value is recognized as a literal:
Set "regex=(Test_Health=(?^!100))"
Due to setlocal enabledelayedexpansion, literal ! chars. inside "..." must be escaped as ^!.
Note that the <name>=<value> token is double-quoted as a whole, to prevent additional interpretation of the value.
Reference variable regex delayed inside the for loop body:
Use !regex! instead of %regex%.
To make the resulting command line more robust - even though it's not needed in this specific case - ensure that the value of regex is enclosed in double quotes (note that %%I - as a mere number - does not need quoting):
Set "to_call=call crv.exe %%I "!regex!""
To put it all together:
#echo off
setlocal enabledelayedexpansion enableextensions
Set "regex=(Test_Health=(?^!100))"
echo Regex is: %regex%
FOR /L %%I IN (1,1,5) DO (
Set "to_call=call crv.exe %%I "!regex!""
echo About to call: !to_call!
)
yields:
Regex is: (Test_Health=(?100))
call crv.exe 1 "(Test_Health=(?!100))"
call crv.exe 2 "(Test_Health=(?!100))"
call crv.exe 3 "(Test_Health=(?!100))"
call crv.exe 4 "(Test_Health=(?!100))"
call crv.exe 5 "(Test_Health=(?!100))"
As for what you did wrong:
%<name>%-style variable references - except for the loop variable (%%I in this case) - are expanded inside the loop body ((...)) before the loop is even parsed, so the values of such variable references can break the loop.
Here's a minimal example that demonstrates the problem:
#echo off
Set "regex=))"
FOR %%I IN ("dummy") DO (
rem !! breaks, because the up-front %regex% expansion causes a syntax error.
echo %regex%
)
Delaying the expansion - by enclosing the variable name in !...!, assuming setlocal enabledelayedexpansion is in effect - bypasses this problem:
#echo off
setlocal enabledelayedexpansion
Set "regex=))"
FOR %%I IN ("dummy") DO (
rem OK - outputs "))"
echo !regex!
)
#ECHO OFF
SETLOCAL
Set "regex="(Test_Health=(?^^^^!100^^)^^)""
SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
echo Regex is: %regex%
FOR /L %%I IN (1,1,5) DO (
Set "to_call=call crv.exe "%%I" %regex%"
echo About to call: !to_call!
)
GOTO :EOF
Sort of depends on what your "desired behaviour" is. Unfortunately, you don't specify.
It's a matter of understanding how cmd works - by substitution, using escape characters and the sequence that this occurs.
The echo reporting the regex won't yield the correct result. Within the for however, each pair of carets is interpreted as a single caret, so the required escapes are as required for the expected output, presumably call crv.exe "5" "(Test_Health=(?!100))" and the like...
I have been translating some shell code to MS-DOS Batch. In my code, I have the sample:
for %%i in (%*) do set "clargs=!clargs! %%i"
If I input the argument "-?" (without the quotation marks), it is not added to clargs. I assume it is because '?' is a wildcard character. Is there anything I can do to ensure that for does not do special things because of the question mark being located in the argument?
You are correct, the wild card characters * and ? are always expanded when used within a FOR IN() clause. Unfortunately, there is no way to prevent the wildcard expansion.
You cannot use a FOR loop to access all parameters if they contain wildcards. Instead, you should use a GOTO loop, along with the SHIFT command.
set clargs=%1
:parmLoop
if "%~1" neq "" (
set clargs=%clargs% %1
shift /1
goto :parmLoop
)
Although your sample is quite silly, since the resultant clargs variable ends up containing the same set of values that were already in %*. If you simply want to set a variable containing all values, simply use set clargs=%*
More typically, an "array" of argument variables is created.
set argCnt=0
:parmLoop
if "%~1" equ "" goto :parmsDone
set /a argCnt+=1
set arg%argCnt%=%1
shift /1
goto :parmLoop
:parmsDone
:: Working with the "array" of arguments is best done with delayed expansion
setlocal enableDelayedExpansion
for /l %%N in (1 1 %argCnt%) do echo arg%%N = !arg%%N!
See Windows Bat file optional argument parsing for a robust method to process unix style arguments passed to a Windows batch script.
#ECHO OFF
SETLOCAL
SET dummy=%*
FOR /f "tokens=1*delims==" %%f IN ('set dummy') DO CALL :addme %%g
ECHO %clargs%
GOTO :eof
:addme
IF "%~1"=="" GOTO :EOF
IF DEFINED clargs SET clargs=%clargs% %1
IF NOT DEFINED clargs SET clargs=%1
SHIFT
GOTO addme
I severly doubt you'll get a completely bullet-proof solution. The above solution will drop separators (comma, semicolon, equals) for instance. Other solutions may have problems with close-parentheses; there's the perpetual % and ^ problems - but it will handle -?
But for your purposes, from what you've shown, what's wrong with
set clargs=%clargs% %*
(No doubt you'll want to process further, but ve haff vays...)