if statement is excuted even the conditions is wrong in batch - windows

SET var=%6
IF NOT "%var%"=="" (
#ECHO "ijklkmn"
EXIT
IF %var:~-2,1%==\ SET var=%var:~0,-2%"
)
If %6 is not defined, then #ECHO "ijklkmn" and is not executed.
But why I still get There should be no 1var:~0。 error???

There are a few batch complexities that are causing your problem.
1) Parenthesized blocks are parsed in one pass. Your entire IF block must parse into valid syntax before the IF test is executed.
2) Substring expansion does weird things if the variable is undefined. The parser expands %var: into nothing, leaving the remainder of the substring expression, and the line parsing becomes entirely out of sync.
IF %var:~-2,1%==\ SET var=%var:~0,-2%"
First %var: expands to nothing, leaving IF ~-2,1%==\ SET var=%var:~0,-2%"
Next %==\ SET var=% is expanded into nothing, leaving IF ~-2,1var:~0,-2%"
Finally, the lone % is not paired, so it is stripped.
The final line after expansion is now IF ~-2,1var:~0,-2", which is invalid syntax.
See https://stackoverflow.com/a/7970912/1012053 for a detailed explanation of how % expansion works.
Your code could be "fixed" by 2 very non-intuitive changes: 1) Reverse the order of the comparison, 2) Add escaped quotes around the SET statement, and 3) add some additional text:
if "\"=="%var:~-2,1%" SET ^"var=%var:~0,-2%"^" ignore
If var is undefined, the above expands to:
if "\" == "~-2,1var:~0,-2"" ignore
The important thing is that the parsed syntax is valid. The ignore is needed because an IF statement needs a command after the condition in order for the parser to see it as valid. It would cause an error if executed because ignore is not a valid command. But it will never get executed because the condition is FALSE (also the outer IF condition is also false).
If var is defined, say with a value of "test\", then the line is expanded as you would think:
if "\" == "\" SET "var=test" ignore
The above executes perfectly because of the way the SET assignment is quoted. Anything after the final quote is ignored.
I say the above will "fix" your code, but I believe there are much better ways to completely restructure your code.
Update: Suggestions for restructuring
It looks to me as though you simply want to set a variable to the value of a quoted parameter, and if the last character in the value (not including quote) is \, then remove that character. It looks like you also have some test code inserted to try to diagnose why your code did not work. Ditching the test code, I recommend the following:
set var=%6
set "test=%var:~-2,1%"
if "%test%"=="\" set var=%var:~0,-2%"
I understand how file names and paths must be quoted to preserve spaces. But I don't like to include the quotes in my variable values. I like to strip the enclosing quotes from my parameters and then add them back only when needed. So my code would look like:
set "var=%~6"
set "test=%var:~-1,1%"
if "%test%"=="\" set "var=%var:~0,-1%"
If delayed expansion is enabled, then a simple solution no longer needs a test variable:
set var=%6
setlocal enableDelayedExpansion
if "!var:~-2,1!"=="\" set "var=!var:~0,-2!""
Or if stripping enclosing quotes:
set "var=%~6"
setlocal enableDelayedExpansion
if "!var:~-1,1!"=="\" set "var=!var:~0,-1!"

I would recommend using the Extension ability defined and everything that dbenham said.
#ECHO off
SET "var=%6"
IF NOT DEFINED var (
ECHO "ijklkmn"
EXIT
)
IF "%var:~-2,1%"=="\" SET "var=%var:~0,-2%""

Related

How to assign a command line parameter which contains ! and & to a variable?

Please consider the following very simple batch script (the file is named test.cmd):
#echo off
set "var1=%~1"
echo %var1%
The script should be called with one command line parameter, should assign the string which is contained in that parameter to a variable, and should output the variable.
As expected, I get an error message when I call this script with a command line parameter which contains an ampersand (&):
C:\Batch>test "a&b"
a
'b' is not recognized as an internal or external command,
operable program or batch file.
The reason for this has been discussed in some other questions here and elsewhere, for example that one; the usual remedy is to use delayed expansion. So I changed the script accordingly:
#echo off
setLocal enableDelayedExpansion
set "var1=%~1"
echo !var1!
Now it works with the parameter from before:
C:\Batch>test "a&b"
a&b
But there is a new problem. When the command line parameter contains an exclamation mark (!), it will be dropped from the output:
C:\Batch>test a!b
ab
This behavior also has been discussed at several places, for example here; the crucial thing to note is that dropping the exclamation mark happens during the assignment, not during the echo.
Despite a lot of research, I did not find a question here which provided an elegant solution for both problems at once. That is, is there an elegant way to assign a command line parameter to a variable when that parameter contains an ampersand AND an exclamation mark?
It seems that I need the delayed expansion to treat the ampersand correctly, but this destroys the exclamation mark.
The only solution I currently see is to not use delayed expansion and to add code to explicitly quote all ampersands in the input string. This would be so ugly that I seriously think that I am missing something here.
As a side note, the reason for the problem actually seems to be that there (IMHO!) is no way to get the command line parameter in a delayed-expanded fashion. The syntax for the first parameter is %~1, there is no such thing as !~1.
Move the setLocal enableDelayedExpansion after the the set„ that's all.
#echo off
set "var1=%~1"
setLocal enableDelayedExpansion
echo !var1!

Use an environment variable in a windows cmd file as substring length parameter

I need to output the first X characters of the content of OLD_ENTRY, say 33 chars. I grab the number of chars with another script. What is, in the following command in a Windows cmd script, the correct syntax to use a variable, say POS, instead of the hardcoded value 33?
echo %OLD_ENTRY:~0,33%
Thanks for any help,
Rip
Alright, I tend to call something like this as "nested variables". Anyway, to expand such nested variables, you need to establish a second parsing or expansion phase, and you need to ensure that the inner variable (POS) becomes expanded first, and the outer one (OLD_ENTRY) becomes expanded during the second phase. There are some options:
Using call:
This option avoids delayed variable expansion, which could be problematic with literal ! symbols, but it is quite slow, and it doubles quoted ^ characters:
In command prompt window:
call echo %^OLD_ENTRY:~0,%POS%%
This looks like "escaping" (^) the outer variable, but actually, this has got nothing to do with true escaping. In command prompt, an undefined variable does not become replaced by an empty string, it is just kept literally. So in the first pass, the undefined variable ^OLD_ENTRY is simply kept (you can verify that by defining such a variable by set "^OLD_ENTRY=something"), scanning for the closing % is skipped after the : for undefined variables strangely, the variable %POS% becomes expanded, and the last (orphaned) % is kept too; immediately after this phase, the escape sequence ^O is recognised, which results in a literal O; so we get echo %OLD_ENTRY:~0,33%, which becomes expanded in the second pass.
In a batch file:
call echo %%OLD_ENTRY:~0,%POS%%%
In the first pass, the first two consecutive percent symbols become replaced by one literal % sign, the variable %POS% becomes expanded, and the remaining two consecutive percent symbols become replaced by one literal % sign too, so we have echo %OLD_ENTRY:~0,33%, which becomes expanded in the second pass.
Using delayed variable expansion:
This is the better option, I think, because it is faster and does not mess around with ^:
echo !OLD_ENTRY:~0,%POS%!
This option works in both command prompt window and batch files. Here the first pass is the normal/immediate expanssion (%) which handles the %POS% variable, so the second pass the delayed expansion (!) receives echo !OLD_ENTRY:~0,33! to expand.
Refer also to this post: How does the Windows Command Interpreter (CMD.EXE) parse scripts?

Replace %~db1 using SET

I'm trying to replace a string passed as an argument to a batch file.
This works:
set PWD="%~dp1"
set "PWD=%PWD:\=/%"
Which puts the passed argument's parent directory path inside PWD variable, and then replaces \ characters to / characters.
Is there a way to achieve this one liner by execution of just one command instead of two?
Something like this: set "PWD=%~dp1:\=/%, however, that doesn't obviously work.
Please elaborate both methods, using DelayedExpansion and not using it.
Did you test this before posting it?
Your code will produce
PWD=\=/
If it produces the "correct result" then it's because you're not clearing pwd at the end of each run (usually by a setlocal directly after the #echo off) and hence the result will be NOT of the current %~dp1 but of the previous setting of pwd.
Your code first sets pwd to be "c:\whatever..." (including the quotes) PLUS the Space before the & and then sets pwd again to the prior value of pwd with \ replaced by /.
cmd will resolve any expression involving %var% first, and then executes the result. With delayedexpansion, !var! is evaluated at run-time and %var% at parse-time.
Since string-manipulation is not allowed on metavariables, the required operation cannot be condensed into a single statement; the value must be passed through an ordinary environment variable first.
To fix your statement, use
set "PWD=%~dp1" & CALL set "PWD=%%PWD:\=/%%"
which will perform the string-manipulation in a subshell after first assigning the value to pwd - moving the quote delimits the command that will be executed - following spaces after the closing quote will not be included in the value assigned.

Quotes in variable - comparison and substitution

I define variable like:
set LOGGING_MANAGER=-Djuli-logback.configurationFile=file:"%CATALINA_HOME%\conf\logback.xml"
Notice, that I wrap %CATALINA_HOME%\conf\logback.xml in double quotes, because path may contain spaces. And at execution step this variable will substitute to java program.
Example of substitution:
path_to_jre/java.exe %LOGGING_MANAGER%
Next code I can't change: (it's from catalina.bat file from Apache Tomcat)
if not "%LOGGING_MANAGER%" == "" ...
This if statement will fail, because variable contains quotes.
There I have one of 2 problems:
if statement fail (error occured).
without quotes when substitute to java program have problems (space is delimiter).
How can I change variable definition so that the path may contain spaces and if statement will work fine?
note: Not tested, but after reading the catalina.bat this is a way to deal with the problem.
If you are running this from a batch file, you can use
set LOGGING_MANAGER_FILE="%CATALINA_HOME%\conf\logback.xml"
set LOGGING_MANAGER=-Djuli-logback.configurationFile=file:%%LOGGING_MANAGER_FILE%%
set _RUNJAVA=CALL "%JRE_HOME%\bin\java.exe"
call catalina.bat
What it does is
Save the file name with quotes in a separate variable
Set the LOGGING_MANAGER variable using a escaped reference of the previous variable. As it will not be expanded, the final content of the LOGGING_MANAGER variable is -Djuli-logback.configurationFile=file:%LOGGING_MANAGER_FILE%
That way, when the if "%LOGGING_MANAGER%"=="" ... is executed, there will be not any problematic quote.
And now the set _RUNJAVA line. Inside catalina.bat, the contents of the _RUNJAVA are used to initialize the _EXECJAVA variable that will launch the server. _RUNJAVA is initialized in setclasspath.bat IF it has not been initialized by the user (code from setclasspath.bat).
rem Don't override _RUNJAVA if the user has set it previously
if not "%_RUNJAVA%" == "" goto gotRunJava
rem Set standard command for invoking Java.
rem Also note the quoting as JRE_HOME may contain spaces.
set _RUNJAVA="%JRE_HOME%\bin\java.exe"
The default value is "%JRE_HOME%\bin\java.exe", we just add a CALL batch command before to force a second iteration of the batch parser that will expand the quoted %LOGGING_MANAGER_FILE% variable in the final command line.
for %%a in ("%CATALINA_HOME%\conf\logback.xml") do set "LOGGING_MANAGER=-Djuli-logback.configurationFile=file:%%~sa"
should set logging_manager appropriately so that you don't have to "quote the filename"
for %%a in ("%CATALINA_HOME%\conf") do net use o: "\\%userdomain%\sharenameforthedrivewherecatalina_homeisresident%%~pa"
set "LOGGING_MANAGER=-Djuli-logback.configurationFile=file:o:\conf\logback.xml"
may also work - if you know the share name for the drive where catalina_home is resident and assuming o: is an unused drive.

Special Characters in Batch File

Special characters in batch files are a pain, but I haven't found the right workaround for properly escaping the first two characters of this particular string I'm trying to pass the application.
SET pass=^&AntiBatchfileString
A_Program.exe /pass=%pass%
Things I have tried:
:: Escaping the escape twice, first for ^, second for &.
SET pass=^^^^&AntiBatchfileString
echo %pass%
:: Combining escapes.
SET first=^^
SET second=^^&AntiBatchfileString
SET pass=%first%%second%
echo %pass%
:: Preventing expansion
SET first=^^
SET second=^^&AntiBatchfileString
SET pass=!first!%second%
echo %pass%
:: I got this to print correctly
SET "pass=^&AntiBatchfileString"
echo ^^%pass%
Still when passing the last one it doesn't accept the login, I don't know what the final output is. That got me thinking maybe it was trying to do another expansion when passing the parameter to the application, so I quoted that as well.
SET "pass=^&AntiBatchfileString"
A_Program.exe "/pass=^^%pass%"
It's still not working, I'm not sure what I'm missing at this point.
Supposing you want the string ^&AntiBatchfileString literally, this is the best set syntax, as most special characters (^ & ( ) < > | and also the standard delimiters , ; = SPACE TAB) lose their particular meaning as soon as ther are placed in between "", and the "" themselves do not become part of the variable value:
set "pass=^&AntiBatchfileString"
This works only as long as the command extensions are on, which is the Windows default anyway (type cmd /? and see the /E option).
When expanding (reading) a variable like "%pass%" (with enclosing ""), special characters are still treated literally.
However, as soon as you expand it like %pass% (no ""), they get back their special meaning. So you have the following options:
Use set "pass=^^^&AntiBatchfileString", where ^^ escapes the literal ^ and ^& the literal & when reading like %pass%.
Enable delayed expansion (see set /? about how it works and setlocal /? or cmd /? about how to enable it), where the variable value is expanded (read) at a point of time where parsing of special characters has already been completed.
I prefer the latter approach, because no special escaping is necessary, and it can also deal with " appearing in the string value (even if unsymmetrically present).
By the way, " can also be escaped by ^", as long as this does not appear within unescaped "".
Nevertheless, % signs cannot be escaped like ^% in a batch file, because percent expansion happens before escaping, but you need to double them like %% to get one literal one each, independent whether or not the string is in between "".
Note that on the console, %% does not work.
Finally, literal ! are consumed by the delayed expansion feature when enabled, therefore you need to pay particular attention to those in case, by escaping them like ^!, or also by intelligently toggling delayed expansion (hence to enable it only when it is actually needed and to disable it otherwise, when a literal string is provided, like in a set command line, for instance, when expanding a standard variable like %pass% and when reading a for variable like %%I (batch file) or %I (console), for example). Of course this is also not the ultimate solution, because you need setlocal and endlocal to enable/disable delayed expansion, which are intended to localise environment changes, so any variable changes since the most recent setlocal command are lost as soon as endlocal is executed (there are some tricks for passing a variable value over the endlocal barrier though).
If you want to use % as a string without escaping in a batch file:
Like %20, you can use %%%20.
git clone "https:// abc.com /D%%%220an"

Resources