Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a parameter to a batch file containing a % without it 'breaking'?

Tags:

batch-file

The Problem

In a main batch file, values are pulled from a .txt file (and SET as values of variables within this batch file). These values may each contain % characters.

These are read from the .txt file with no issues. However, when a variable with a value containing a % character is passed to a second batch file, the second batch file interprets any % characters as a variable expansion. (Note: There is no control over the second batch file.)

Example

echo %PERCENTVARIABLE%

Output: I%LOVE%PERCENT%CHARACTERS%

When passed to a second file and then echo'ed, would (probably) become IPERCENT, as it interprets %LOVE% and %CHARACTERS% as unset variables.


Research

I found the syntax to find and replace elements within a string in a batch file, as I thought I could potentially replace a % character with %% in order to escape it. However I cannot get it to work.

The syntax is -

set string=This is my string to work with.
set string=%string:work=play%
echo %string%

Where the output would then be This is my string to play with..


Questions

  1. Is it possible to escape % characters using the find and replace syntax in a variable? (If not, is there another way?)
  2. Is it advisable to do so? (Could using these escape characters cause any issue in the second batch file which (as mentioned above) we would have no control over?)
  3. Is there another way to handle this issue, if the above is not possible?
like image 450
Eilidh Avatar asked Oct 18 '22 01:10

Eilidh


2 Answers

There are no simple rules that can be applied in all situations.

There are a few issues that make working with string literals in parameters difficult:

  1. Poison characters like &, |, etc. must be escaped or quoted. Escaping is difficult because it can be confusing as to how many times to escape. So the recommendation is to usually quote the string.
  2. Token delimiters like <space>, <tab>, =, ; and , cannot be included in a parameter value unless it is quoted.
  3. A CALL to a script will double any quoted % characters, and there is no way to prevent this. Executing a script without CALL will not double the % characters. But if a script calls another script and expects control to be returned, then CALL must be used.

So we have a catch-22: On the one hand, we want to quote parameters to protect against poison characters and spaces (token delimiters). But to protect percents we don't want to quote.

The only reliable method to reliably pass string literals without concern of value corruption is to pass them by reference via environment variables.

  1. The value to be passed should be stored in an environment value. Quotes and/or escapes and/or percent doubling is used to get the necessary characters in the value, but it is very manageable.
  2. The name of the variable is passed in as a parameter.
  3. The script accesses the value via delayed expansion. For example, if the first parameter is the name of a variable containing the value, then it is accessed as !%1!. Delayed expansion must be enabled before that syntax can be used - simply issue setlocal enableDelayedExpansion.

The beauty of delayed expansion is you never have to worry about corruption of poison characters, spaces, or percents when the variable is expanded.

Here is an example that shows how the following string literal can be passed to a subroutine
"<%|,;^> This & that!" & the other thing! <%|,;^>

@echo off
setlocal enableDelayedExpansion
set "parm1="^<%%^|,;^^^^^> This ^& that^^!" & the other thing^! <%%|,;^^^>"
echo The value before CALL is !parm1!
call :test parm1
exit /b

:test
echo The value after  CALL is !%1!

-- OUTPUT --

The value before CALL is "<%|,;^> This & that!" & the other thing! <%|,;^>
The value after  CALL is "<%|,;^> This & that!" & the other thing! <%|,;^>

But you state that you have no control over the 2nd called script. So the above elegant solution won't work for you.

If you were to show the code of the 2nd script, and show exactly what value you were trying to pass, then I might be able to give a solution that would work in that isolated situation. But there are some values that simply cannot be passed unless delayed expansion is used with variable names. (Actually, another option is to put the value in a file and read the value from the file, but that also requires change to your 2nd script)

like image 117
dbenham Avatar answered Oct 21 '22 02:10

dbenham


may be...?

input.txt

I%LOVE%PERCENT%CHARACTERS%

batch1.bat

@echo off
setlocal enableDelayedExpansion
set/P var=<input.txt
echo(In batch 1 var content: %var%

set "var=!var:%%=%%%%!"
call batch2.bat "%var%"
endlocal
exit/B

batch2.bat

@echo off
set "var=%~1"
echo(In batch 2 var content: %var%
exit/B
like image 32
elzooilogico Avatar answered Oct 21 '22 02:10

elzooilogico