Windows Shell Scripting: Check for batch options containing double quotes - windows

Greetings, dear Experts!
I want to check the existense of parameter (or argument) sting at all in my batch script:
if "%*"=="" findstr "^::" "%~f0"&goto :eof
This works fine if none of parameters is enclosed in double quotes. For example:
test.bat par1 par2 ... --- works
but
test.bat "par 1" par2 ... --- fails
My question are:
1) Is there any way to overcome this instead of requirement for use to use non-double-quoted symbol to specify "long" arguments and then use string substitution?
2) Can I ever use "if" to compare two strings containing both double quotes and spaces?
Your prompt and clear reply would be very much appreciated.

~ will strip the double quotes, but does not work on %*, but if you just want to know if there are no parameters, just checking %1 should be enough
if "%~1"==""
You might want to call setlocal ENABLEEXTENSIONS first to make sure extensions are on (required for ~)

Thank you, Andres.
Here are two more pieces of code to check the existence and number of passed parameters:
set ArgumentString=%*
if not defined ArgumentString findstr "^::" "%~f0"&goto :eof
if "%ArgumentString:"=%"=="" findstr "^::" "%~f0"&goto :eof
set NumberOfArguments=0
for /l %%N in (1,1,9) do (
call set CurrentArgument=%%%%N
if defined CurrentArgument set /a NumberOfArguments += 1
)
if %NumberOfArguments% NEQ %N% findstr "^::" "%~f0"&goto :eof
Here variable N contains needed number of parameters.
Hope this helps for somebody!

Since Andrey's answer fails to correctly count arguments, here is a way that does work, even with more than 9 arguments (and it even perserves the original ones, so %1 still points to the first one by moving the shifting into a subroutine):
#echo off
call :get_num_args %*
echo %NumArgs%
goto :eof
:get_num_args
set NumArgs=0
:loop
if not [%1]==[] (set /a NumArgs+=1) else (goto :eof)
shift
goto loop
goto :eof

Related

Find and replace algorithm for string in text file using batch script, works, but stopping when `<`, `>`, or `|` characters appear

I've been trying to figure out how to replace an entire line in a text file that contains a certain string using a Batch Script. I've found this solution provided by another user on Stack Overflow, which does the job, however, it just stops iterating through the text file at some random point and in turn, the output file is left with a bunch of lines untransferred from the original file. I've looked character by character, and line by line of the script to figure out what each part exactly does, and can not seem to spot what is causing this bug.
The code provided, thanks to Ryan Bemrose on this question
copy nul output.txt
for /f "tokens=1* delims=:" %%a in ('findstr /n "^" file.txt') do call :do_line "%%b"
goto :eof
:do_line
set line=%1
if {%line:String =%}=={%line%} (
echo.%~1 >> output.txt
goto :eof
)
echo string >> output.txt
The lines it is stopping at always either contain < or > or both and lines with | will either cause it to stop, or sometimes it will delete the line and continue.
To do this robustly, Delayed expansion is necessary to prevent "poison" characters such as < > & | etc being interpreted as command tokens.
Note however that delayed expansion should not be enabled until after the variable containing the line value is defined so as to preserve any ! characters that may be present.
The following will robustly handle all Ascii printable characters *1, and preserve empty lines present in the source file:
#Echo off
Set "InFile=%~dp0input.txt"
Set "OutFile=%~dp0output.txt"
Set "Search=String "
Set "Replace="
>"%OutFile%" (
for /F "delims=" %%G in ('%SystemRoot%\System32\findstr.exe /N "^" "%InFile%"') do (
Set "line=%%G"
call :SearchReplace
)
)
Type "%OutFile%" | More
goto :eof
:SearchReplace
Setlocal EnableDelayedExpansion
Set "Line=!Line:*:=!"
If not defined Line (
(Echo()
Endlocal & goto :eof
)
(Echo(!Line:%Search%=%Replace%!)
Endlocal & goto :eof
*1 Note - Due to how substring modification operates, You cannot replace Search strings that:
contain the = Operator
Begin with ~

Windows Batch: Turning DelayedExpansion on/off inside a loop and preserving the value of variables while doing so? [duplicate]

This question already has answers here:
Make an environment variable survive ENDLOCAL
(8 answers)
Closed 3 years ago.
I'm trying to use a FOR loop to read the lines in a text file, but I also need to keep track of some variables and evaluate them. The easiest way to do that is by enabling DelyaedExpansion. Actually, it seems to be the ONLY way as everything else I've tried in relation to variables fails miserably if I don't use it. Unfortunately, this means that if any of the lines of text in the file contain exclamation points, they will be stripped out.
I thought I had found a solution by reading a line of text and putting it into a variable, THEN enabling DelayedExpansion, doing the variable operations, and finally using ENDLOCAL & SET VARIABLE=%VARIABLE% to preserve the value. Unfortunately that doesn't seem to work if the ENDLOCAL statement is inside a loop.
For example;
echo off
for /F "delims=" %%F in (test.txt) do (
set Line=%%F
setlocal enabledelayedexpansion
set /a Count=Count+1
echo !Count! - !Line!
endlocal & set Count=%Count%
)
echo Total: %Count%
Each time the loop repeats, the value of "Count" is reset to zero.
If I move the SETLOCAL command before the FOR command, it will strip any "!" from the text, which is unacceptable.
Please note: The example above is only a small part of a much larger script that does many things with the variables inside the loop. I have boiled the problem down to the bare minimum to make it easy to understand. I need to preserve "!" in text read from a file while also being able to perform multiple variable operations within each loop.
So I either need a way to read text from a file, one line at a time, with DeleyedExpansion enabled AND preserve any "!" in the text, or preserve the value of variables that are defined within the SETLOCAL/ENDLOCAL commands within a loop.
With Help from dbenham and his answer here, There is a Solution that exists for this Scenario.
The key, as Dave has Shown, is in Setting the variables PRIOR to using SetlocalEnableDelayedExpansion so that ! is preserved.
#echo off
Set "count=0"
For /F "delims=" %%F in (test.txt) do (
Call :LineParse "%%~F"
)
REM The Below Loop demonstrates Preservation of the Values
Setlocal EnableDelayedExpansion
For /L %%a in (1,1,!count!) DO (
ECHO(!line[%%a]!
)
Endlocal
pause
exit
:LineParse
Set /a count+=1
Set "Line[%count%]=%~1"
Setlocal EnableDelayedExpansion
ECHO(!Line[%count%]!
ECHO(Total: !count!
(
ENDLOCAL
)
GOTO :EOF
There are still a few characters that will not be parsed as desired with this Method, noted in test.txt:
test.txt
Safe Characters: ! > * & ` ' . ] [ ~ # # : , ; ~ } { ) ( / \ ? > < = - _ + $ |
problem Characters: ^ "" %%
problem examples:
line disappears " from single doublequote
but not "" from escaped doublequote
%% will not display unless escaped. % unescaped Percent Symbols will attempt to expand %
caret doubles ^ ^^ ^^^
Don't need to complicate...
Just replace:
echo/ to set /p
setlocal enabledelayedexpansion to cmd /v /c
#echo off
for /F "delims=" %%F in ('type test.txt')do set /a "Count+=1+0" && (
(echo/ & cmd /v/s/c "set/p "'=!Count! - %%F"<nul")>>".\newfile.txt")
cmd /v /c echo/ Total: !Count! && call set "Count="<nul && goto :EOF

Batch script to read the last word of a sentence

I want to get last word from a sentence or file path
I tried solutions from other articles and forums but can't seem to figure it out. here is what I have so far
#echo off
set a="C:\Some\Random\File\Path.txt"
echo %a%
set b=%a:\= %
echo %b%
for /f "tokens=*" %%a in (%b%) do #echo %%a %%b %%c
pause
All I need is the last word of what ever file path I put in %a%
Just to answer specifically on your question:
#echo off
set "a=C:\Some\Random\File\Path.txt"
for %%i in (%a%) do echo %%~nxi
pause
Also see how I used the double quotes on the setting of the variable, This allows us to get rid of any wanted whitespace after the value, and also if we add the quotes after the equal, they become part of the value, which is not what we really want.
To understand the variable references, for from cmdline for /? I suggest you also see set /?
Additionally, based on your comment, if you want to set it as a viarble, simply do it :)
#echo off
set "a=C:\Some\Random\File\Path.txt"
for %%i in (%a%) do set variablename=%%~nxi
echo %variablename%
pause
While Gerhards solution IS preferable (for path), your approach wasn't that bad.
You just have to use a simple for,
so it iterates over the space seperated elements which therefor have to be unquoted.
Repeatedly setting the same variable will have the last value/word persisting.
:: Q:\Test\2019\01\18\SO_54246604.cmd
#echo off
set "a=C:\Some\Random\File\Path.txt"
echo %a%
for %%a in (%a:\= %) do Set "b=%%a"
echo %b%
pause
> SO_54246604.cmd
C:\Some\Random\File\Path.txt
Path.txt

Batch file - How to find quotes in a string?

When given an environment variable that may or may not be quoted I need to remove the quotes.
For example, I may be given:
JAVA_HOME=C:\Program Files\Java\jdk1.8.0_144
JAVA_HOME="C:\Program Files\Java\jdk1.8.0_144"
I am trying to use findstr and it seems to work well from cmd.exe but not from my batch file. This is what I have in test1.bat:
#echo off
echo.%JAVA_HOME% | findstr \^" 1>nul
if ERRORLEVEL 0 (
echo Found Quotes
)
if ERRORLEVEL 1 (
echo No Quotes found
)
The problem I have is that the ERRORLEVEL always seems to be 0 and I always get the message "Found Quotes" when I run test1.bat.
I have this code to remove quotes for /f "delims=" %%G IN (%JAVA_HOME%) DO SET JAVA_HOME=%%G which works find, but only when there are quotes, hence needing to get the above findstr conditions working correctly.
Batch files already have the ability to remove quotes on for-loop variables like %%G in your example, so you do not need any of that findstr logic. The reason why your for doesn't work is because you're not quoting the %JAVA_HOME%. You always have to quote arguments that have spaces.
From help for, the syntax is:
FOR /F ["options"] %variable IN ("string") DO command
[command-parameters]
And on automatic variables like %%G, the tilde operator removes quotes if they are present.
From help for:
In addition, substitution of FOR variable references has been
enhanced. You can now use the following optional syntax:
%~I - expands %I removing any surrounding quotes (")
So with these 2 pieces of information, change your for loop to this to remove quotes from %JAVA_HOME%.
for /f "delims=" %%G IN ("%JAVA_HOME%") DO SET "JAVA_HOME=%%~G"
You typically have to test for other values for ERRORLEVEL before testing for 0, IIRC. This works perfectly for me:
#echo off
echo.%JAVA_HOME% | findstr \^" 1>nul
if ERRORLEVEL 1 (
echo No quotes found
goto :eof
)
if ERRORLEVEL 0 (
echo Found quotes
)
NOTE: Removing the goto :eof means you get both lines output by echo if there is no match.
#ECHO OFF
SETLOCAL
SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0_144
SET "java_home=%java_home:"=%"
SET j
ECHO -------------------------------------------
SET JAVA_HOME="C:\Program Files\Java\jdk1.8.0_144"
SET "java_home=%java_home:"=%"
SET j
GOTO :EOF
Either way, java_home ends up "nude".

Batch For Loop: Use wildcard character in string

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...)

Resources