Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
849 views
in Technique[技术] by (71.8m points)

string - What is the difference between "..." and x"..." in an IF condition in a Windows batch file?

I recently found the post Find if substring is in string (not in a file) where it is stated that considering

@setlocal enableextensions enabledelayedexpansion
@echo off
set str1=%1
if not x%str1:bcd=%==x%str1% echo It contains bcd
endlocal

then

the x before the two sides of the equality is to ensure that the string bcd works okay. It also protects against certain "improper" starting characters.

However, I haven't found any explanation about the actual effect of this x. So what is the difference between x"%string%" and "%string%"?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

That is simply a very bad coded string comparison. The x on both sides makes it possible to compare the two strings even if %str1:bcd=% or %str1% is substituted by Windows command processor on parsing entire command line by an empty string before execution of command IF.

But the batch file execution is nevertheless exited immediately by cmd.exe because of a syntax error in case of value of environment variable str1 contains a space character or "&<>|.

Enclosing an argument string in double quotes results in getting all characters except percent sign and with enabled delayed environment variable expansion also the exclamation mark interpreted as literal character including space which is outside a double quoted string interpreted as argument string separator.

So much better is:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" goto EndBatch
set "str1=%~1"
if not "%str1:bcd=%" == "%str1%" echo It contains bcd
:EndBatch
endlocal

The first argument of the batch file is compared first without surrounding double quotes with an empty string. So if the batch file is started without any argument or with just "" as first argument string, Windows command processor executes the command GOTO resulting in restoring previous environment pushed on stack with command SETLOCAL and exits the batch file.

Otherwise the batch file is called really with an argument string. This argument string is assigned to environment variable str1 with removing surrounding double quotes if there are one. So on calling batch file with argument test the value test is assigned to environment variable str1 and on calling it with "another test" the value another test without the double quotes is assigned to str1. And even on calling the batch file with wrong coded argument string "bcd test (missing second ") just bcd test is assigned to the environment variable str1.

The IF condition compares the value of environment variable str1 with all occurrences of bcd removed with the unmodified variable value. The double quotes around the two strings make it possible to compare the two strings even on containing space or ampersand or the redirection operators <>|. The command IF includes the double quotes on comparing the two strings.

So is this code now safe?

No, it is not in case of somebody calls the batch file invalid with test_bcd" as argument string on which first double quote is missing. In this case the first IF command line executed by cmd.exe is:

if "test_bcd"" == "" goto EndBatch

The trailing " of the wrong specified argument string is not removed by cmd.exe and cause a syntax error on this command line on execution as it can be seen on running the batch file from within a command prompt window with first line modified to @echo on.

One solution without using delayed environment variable expansion is:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "str1=%~1"
if not defined str1 goto EndBatch
set "str1=%str1:"=%"
if not defined str1 goto EndBatch
if not "%str1:bcd=%" == "%str1%" echo It contains bcd
:EndBatch
endlocal

This code makes sure that str1 does not contain any double quote before executing the IF command comparing the strings.

Another solution is using delayed environment variable expansion:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "str1=%~1"
if not "!str1:bcd=!" == "!str1!" echo It contains bcd
endlocal

That looks better as the above code without usage of delayed environment variable expansion. But it does not work as expected if the the argument string is for example "!Hello!" because in this case the if not condition is also true and output is therefore the message It contains bcd although the string !Hello! does not contain bcd.

The solution is:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "str1=%~1"
setlocal EnableDelayedExpansion
if not "!str1:bcd=!" == "!str1!" echo It contains bcd
endlocal
endlocal

Delayed expansion is not enabled on assigning the argument string to environment variable str1 which results in getting the exclamation marks in string "!Hello!" interpreted as literal characters. Then delayed expansion is enabled for making the string comparison with using delayed environment variable expansion which avoids that the command line itself is modified by cmd.exe before execution of IF command.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /? ... explains %~1, not so good as done here.
  • echo /?
  • endlocal /?
  • goto /?
  • if /?
  • set /?
  • setlocal /?

See also:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...