1

I am trying to call a command from a batch file which is reading in lines from a file.
This is working correctly, except for when the line contains the redirection character >.

Is there a way to tell call to escape this character, or to replace it within the content of the for loop?

I've looked at setlocal enabledelayedexpansion and (when the call is updated to use ! it still doesn't work).

set /p status=<%tmp_file%

for /f "delims=*" %%a in (%tmp_file%) do (
  echo "%%a"
  call check.bat "%%a"
  echo.

  if not "%errorlevel%" == "0" do exit /b 1
)

This produces the following output (when check.bat echo's %1)

"a"
"a"

"b -> b"
"b -"

I've tried to replace > within %%a but I'm not entirely sure how this can be achieved, each time i try, it yields an empty string, i.e.

set line=%a:^>=¬%

EDIT 1
Some more clarification (it appears to only be the case if %1 is set to a variable, and then that variable is used)?:

check.bat:

 set rawinput=%1
 set input=%~1

 echo %1
 echo "%rawinput%"
 echo "%input%"
 echo.

This yields the following output, although im not quite sure why setting %1 to a variable causes it to mangle the value?

 "a"
 "a"
 ""a""
 "a"

 "b -> b"
 "b -> b"
 "b -"

Interestingly b -> b is only output 2 times, echo "%rawinput%" is not showing at all. Both echo "%input%" and echo "%rawinput%" are writing to a file named b.

This means that the check.bat for b -> b must be interpreted as:

 echo "b -> b"
 echo ""b -> b""
 echo "b -> b"        REM - this is what 'should' be happening, however does appear to be the case, as it writes '' to a file named b
 echo.

If anyone can shed light on why echo "b -> b" in a batch file does not appear to be behaving it would be greatly appreciated.

9
  • I don't see how your posted code can give your posted results. The quotes should protect the > so it doesn't need escaping. I suspect either your code or your results is different than what you posted, or else check.bat is more complicated than you describe. Commented Nov 19, 2012 at 14:47
  • echo "%%a" yields the first line in each pair, in check.bat the only line is echo %1 which yields the second line in each pair. As such the > is being lost/used somewhere. Commented Nov 19, 2012 at 15:04
  • Yes, and I tried your code with tmp_file containing two lines: a and b -> b (no quotes in file). Your IF statement is messed up, but other than that it works fine. Both lines printed out just fine by check.bat. I do not get the results you show. There must be something about your situation or your code that you are not telling us. If your file contains quoted "b -> b", then echo "%%a" should produce ""a"", and check.bat should create a file named "b" containing ""b -. Commented Nov 19, 2012 at 19:02
  • @dbenham the if statement is so that if check.bat sets an exit code of non zero then the for loop should exit. This works fine in my examples. Commented Nov 19, 2012 at 21:09
  • @dbenham Not sure why there is a discrepancy between what you're seeing and what is happening here. I will check the encoding and line endings of the temp file but otherwise this is the same as the inputs causing my issue. Commented Nov 19, 2012 at 21:12

2 Answers 2

3

Do not try to use call by value, that's nearly impossible with batch.
Simply set a new variable with the value and then use the variable name.

for /f "delims=" %%a in (%tmp_file%) do (
  set "line=%%a"
  call :check
)
exit /b

:check
setlocal EnableDelayedExpansion
echo(!line!
endlocal
exit /b

Edit: Your problem

You try to call a function with the content "b -> b" using
call check.bat "%%a" this will expand to
call check.bat ""b -> b"" and that's the problem!
As the > redirect character is now outside of the quotes, it will redirect the output of check.bat to the file b.
You can escape a special character (outside of quotes) when using a call, but this is a bit advanced and has many problems.

So, I would recommend again, not using directly the content in a call.
You could use a reference, like

for /f "delims=" %%a in (%tmp_file%) do (
  set "line=%%a"
  call :check line
)

check.bat

setlocal EnableDelayedExpansion
set "var=!%1!"
echo(!var!
Sign up to request clarification or add additional context in comments.

10 Comments

That is generally good advice, but it does not explain the OP's results.
@jeb, this answer does work, however, it requires a goto:eof before endlocal otherwise, it exits at the end of the first iteration, as it falls over exit /b
@jeb: Because FOR replaceable parameters are global, they also exist in the called subroutine. This way, for ... %%a in ... do call :check works with echo %%a placed in the subroutine. Interesting! Isn't it?
@Aacini Yes, but it does only works in a subroutine, if the echo %%a itself is itself again in an (other) for-loop
+1 for the suggestion to pass values by reference, along with demonstration code. But the output mangling is occurring within check.bat, not in the CALL statement. See my answer
|
2

You have multiple problems in your modified check.bat

The quotes are a state machine in that the 1st quote turns quoting semantics ON, the 2nd OFF, the 3rd ON, etc. An unquoted (and unescaped) > is interpreted as an instruction to redirect output.

I've heavily modified check.bat by putting in a bunch of ECHO statements to provide a narrative of what is happening. Your original code is embedded in my version as all CAPS.

Here is the output of my modified version, which I hope explains why you are getting your results. I want you to read this output before you try to understand how my modified check.bat produces this output.

"a"
---------------------------------------------
Executing: SET RAWINPUT=%1  which expands to  SET RAWINPUT="a"
Here is the result:
rawinput="a"

Executing: SET INPUT=%~1  which expands to  SET INPUT=a
Here is the result:
input=a

Executing: ECHO %1  which expands to  ECHO "a"
"a"

Executing: ECHO "%RAWINIPUT%"  which expands to  ECHO ""a""
""a""

Executing: ECHO "%INPUT%"  which expands to  ECHO "a"
"a"


"b -> b"
---------------------------------------------
Executing: SET RAWINPUT=%1  which expands to  SET RAWINPUT="b -> b"
Here is the result:
rawinput="b -> b"

Executing: SET INPUT=%~1  which expands to  SET INPUT=b -> b
Here is the result:
input=b -

Also created a file named "b" because the output of the command
is redirected. The file is empty because SET has no output.

 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\Users\Public\utils

11/20/2012  09:30 AM                 0 b
               1 File(s)              0 bytes
               0 Dir(s)  316,104,765,440 bytes free

Executing: ECHO %1  which expands to  ECHO "b -> b"
"b -> b"

Executing: ECHO "%RAWINIPUT%"  which expands to  ECHO ""b -> b""

There is no screen output because output was redirected to file "b"
Here are the contents of "b":
""b -

Executing: ECHO "%INPUT%"  which expands to  ECHO "b -"
"b -"

Here is my modified check.bat that produces the output above.

setlocal enableDelayedExpansion
set value=%1
set "unquotedValue=%~1"

echo ---------------------------------------------
echo Executing: SET RAWINPUT=%%1  which expands to  SET RAWINPUT=!value!
SET RAWINPUT=%1
echo Here is the result:
set rawinput
echo.

echo Executing: SET INPUT=%%~1  which expands to  SET INPUT=!unquotedValue!
SET INPUT=%~1
echo Here is the result:
set input
if exist b (
  echo.
  echo Also created a file named "b" because the output of the command
  echo is redirected. The file is empty because SET has no output.
  echo.
  dir b
  del b
)
echo.

echo Executing: ECHO %%1  which expands to  ECHO !value!
ECHO %1
echo.

echo Executing: ECHO "%%RAWINIPUT%%"  which expands to  ECHO "!rawinput!"
ECHO "%RAWINPUT%"
if exist b (
  echo.
  echo There is no screen output because output was redirected to file "b"
  echo Here are the contents of "b":
  type b
  del b
)
echo.

echo Executing: ECHO "%%INPUT%%"  which expands to  ECHO "!input!"
ECHO "%INPUT%"
echo.


You can get your desired results via a variable if you do not add any quotes in your check.bat

set rawinput=%1
echo %rawinput%

But all hell can break loose if your original text file includes its own quotes.

That is the reason jeb suggests storing each line in a variable within your main routine and then pass the name of the variable to check.bat. Within check.bat you can use delayed expansion to safely work with any value without worrying about quotes or escapes.

1 Comment

Thanks for your help, jeb's initial initial lead me in the right direction hence the accept. For completeness, however, the input file does not contain quotes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.