Optional arguments parsed in batch using shift - batch-file

I would like to know if it is possible to make a more elegant solution for a batch file accepting arguments from another script.
Looked at the solution here: Windows Bat file optional argument parsing, but can't make it work.
I have 19 arguments passed to the batch file. Arguments from the 7th are optional (come from checkboxes).
I have a working code with this structure:
REM arg7 parsing
set LOGFILE=%7
if not "%LOGFILE%" == "LOGFILE" (
set LOGFILE=
) else (
shift /7
)
REM arg8 parsing
set ARG8=%7
if not "%ARG8%" == "ARG8" (
set ARG8=
) else (
shift /7
)
REM arg9 parsing
set ARG9=%7
if not "%ARG9%" == "ARG9" (
set ARG9=
) else (
shift /7
)
And so on for each optional argument.
But now there are a lot of lines of similar code and it feels to me it could be optimized somehow. I'm not very familiar with batch scripting, so my tries of storing arguments in a list and trying to apply the above mentioned solution didn't work.

Based on what you've posted, try this:
SET "_="
FOR %%I IN (
LOGFILE ARG8 ARG9
) DO IF "%7" EQU "%%I" (SET "%%I=%7" & SET _=T)
IF DEFINED _ SHIFT /7
Just insert the other arguments on line three as is obvious.

#echo off
REM process optional parameters:
shift&shift&shift&shift&shift
rem shift /5 :: couldn't get this to work...
set narg=6
:loop
shift
set /a narg+=1
if "%1" == "" goto :done
set arg%narg%=%1
goto :loop
:done
echo no more parameters
set arg

Related

Define Batch process parameters

I define some parameter when calling a batch file:
:: Usertype:I,C
set Usertype=%~1
set Deltaval=%~2
If Usertype=="C" set Gender=NA
set Gender=%~3
If Gender==NA
(
goto
END
)
However, I got issue at line If Usertype=="C" set Gender = NA with following error:
The syntax of the command is incorrect.
Are there any solution to it?
So this is what I am trying to tell you, you need to use % in order to define the words you use as variables and not see them as plain text.
:: Usertype:I,C
set "Usertype=%~1"
set "Deltaval=%~2"
If /i "%Usertype%"=="C" If /i "%Usertype%"=="I" (
set "Gender=NA"
) else (
set "Gender=%~3"
)
If "%Gender%"=="NA" goto :eof
Or you can do
:: Usertype:I,C
set "Usertype=%~1"
set "Deltaval=%~2"
If /i not "%Usertype%"=="C" If /i not "%Usertype%"=="I" do something

how to include or in a loop in batch script

i'm trying to write in a folder the result of my my compilation : if there is one type of file (file1 or file2) it means that my compilation succeded. And I want to report this result in a txt file
I tried to do it with a flag using booleans but it just does not work. i'm parsing my folders and in each output/NameofaRun I'm looking if there is file1 or file2.
for /D %%X in (%1\*) do (
set flag = 0
if exist %%X\OUTPUT\%2\file1.txt ( flag = 1)
if exist %%X\OUTPUT\%2\file2.txt ( flag = 1)
if %flag% == 1 ( echo %%X>>%3\Compilation_Check.txt Succeeded )
if %flag% == 0 (
echo %%X>>%3\Compilation_Check.txt Failed Warning
)
)
I want to write in "compilation_check.txt" the result of my txt. and I'm running this batch with path to folders/name of folder as parameters
Your logic is correct; several details not...
The set command get as variable name all characters before the equal sign including spaces. You should use set flag=0 or, better yet, set "flag=0".
It is convenient to enclose file names between quotes in order to avoid problems if any name may include spaces...
You missed the set command when you change flag value.
When a variable value changes inside a (block), the new value can not be accessed via %var%, but using !var! syntax AND including setlocal EnableDelayedExpansion command at beginning of the program. There are a lot of detailed explanations about this point; look for "delayed expansion".
This is your code with previous modifications:
setlocal EnableDelayedExpansion
for /D %%X in (%1\*) do (
set "flag=0"
if exist "%%X\OUTPUT\%2\file1.txt" ( set "flag=1" )
if exist "%%X\OUTPUT\%2\file2.txt" ( set "flag=1" )
if !flag! == 1 ( echo %%X Succeeded >> "%3\Compilation_Check.txt")
if !flag! == 0 (
echo %%X Failed Warning >> "%3\Compilation_Check.txt"
)
)
However, I would do it this way:
for /D %%X in (%1\*) do (
set "anyFile="
if exist "%%X\OUTPUT\%2\file1.txt" set "anyFile=1"
if exist "%%X\OUTPUT\%2\file2.txt" set "anyFile=1"
(if defined anyFile (
echo %%X Succeeded
) else (
echo %%X Failed Warning
)) >> "%3\Compilation_Check.txt"
)
You are not using the value of flag variable. The if defined command allows to check for the existence of a variable; this "trick" allows to avoid the Delayed Expansion problem...
You may enclose several commands in a (block) so the output of all of them is redirected to the same file...

Batch Get String between 2 characters

I have a text file with a content similar like this:
test.txt:
FIPS job <1532602344643_1> of size <134> successfully created.
<134> files successfully exported.
<0> files failed.
I want to store the string between the first two angle brackets in a variable. In this example it is 1532602344643_1. The string should have the same length but in all other brackets the length could change.
Im new to this so can someone help me?
Here the batch file that do what you need:
#echo off
set TEXT_FILE=text.txt
setlocal enabledelayedexpansion
set modules=
for /f "tokens=*" %%i in (%TEXT_FILE%) do (
set "tmp=%%~i"
set modulesFromLine=
call :getNext modulesFromLine "!tmp!"
if "-!modules!"=="-" (
set modules=!modulesFromLine!
) else (
set modules=!modules!,!modulesFromLine!
)
)
endlocal & set REZULT=%modules%&
echo. %REZULT%
exit /b 0
:getNext
setlocal
set accumulator=
set "tmp=%~2"
set "tmp=!tmp:*<=!"
set "tail=>!tmp:*>=!"
call set "module_name=%%tmp:!tail!=%%"
if not "-!module_name!"=="-" (
call :getNext %1 "!tail!"
if "-!%1!"=="-" (
set "accumulator=!module_name!"
) else (
set "accumulator=!module_name!,!%1!"
)
)
endlocal & set %1=%accumulator%&
exit /b 0
Put your file to TEXT_FILE and take RESULT. Remember that string should not contain ".
If you want to read someting about batch wiki is good for start.

how to match the command line arguments in the batch file

The following script commands check matching the command line argument %1 against the fixed word ala
<code>
#echo off
set one=%1
set two=%2
If NOT "%one%"=="%one:ala=%" ( echo the first argument contains the word "ala")
else ( echo no matching ! )
</code>
How to replace the fixed word "ala" with an argument %2 from the command line instead.
(because the simple replacement ala with %2 doesnt work).
Is there any better solution for comparing the argument strings ?
#ECHO OFF
SETLOCAL
ECHO %~1|FIND "%~2">NUL
IF ERRORLEVEL 1 (
ECHO "%~2" NOT found IN "%~1"
) ELSE (
ECHO "%~2" WAS found IN "%~1"
)
GOTO :EOF
Use the find facility. This avoids delayedexpansion but is relatively slow.
You need to use delayed expansion to accomplish that type of string replacement.
#echo off
setlocal enabledelayedexpansion
set "one=%~1"
set "two=%~2"
If NOT "%one%"=="!one:%two%=!" (
echo the first argument contains the word "%two%"
) else (
echo no matching
)
And you could also do it without delayed expansion using a technique with the CALL command.
#echo off
set "one=%~1"
CALL set "two=%%one:%~2=%%"
If NOT "%one%"=="%two%" (
echo the first argument contains the word "%two%"
) else (
echo no matching
)

How to receive even the strangest command line parameters?

as discussed in an other thread How to avoid cmd.exe interpreting shell special characters like < > ^
it is not easy to get all parameters from the command line.
A simple
set var=%1
set "var=%~1"
are not enough, if you have a request like
myBatch.bat abc"&"^&def
I have one solution, but it needs a temporary file, and it is also not bullet proof.
#echo off
setlocal DisableDelayedExpansion
set "prompt=X"
(
#echo on
for %%a in (4) do (
rem #%1#
)
) > XY.txt
#echo off
for /F "delims=" %%a in (xy.txt) DO (
set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param='!param!'
It fails with something like myBatch.bat %a, it display 4 not the %a
in this situation a simple echo %1 would work.
It's obviously the for-loop but I don't know how to change this.
Perhaps there exists another simple solution.
I don't need this to solve an actual problem, but I like solutions that are bullet proof in each situation, not only in the most cases.
I don't think anyone found any holes in this, except for the inability to read newlines in the parameters:
#echo off
setlocal enableDelayedExpansion
set argCnt=1
:getArgs
>"%temp%\getArg.txt" <"%temp%\getArg.txt" (
setlocal disableExtensions
set prompt=#
echo on
for %%a in (%%a) do rem . %1.
echo off
endlocal
set /p "arg%argCnt%="
set /p "arg%argCnt%="
set "arg%argCnt%=!arg%argCnt%:~7,-2!"
if defined arg%argCnt% (
set /a argCnt+=1
shift /1
goto :getArgs
) else set /a argCnt-=1
)
del "%temp%\getArg.txt"
set arg
The above comes from a lively DosTips discussion - http://www.dostips.com/forum/viewtopic.php?p=13002#p13002. DosTips user Liviu came up with the critical SETLOCAL DisableExtensions piece.
The code below is based on the rambling Foolproof Counting of Arguments topic on DosTips and this answer by jeb:
#echo off & setLocal enableExtensions disableDelayedExpansion
(call;) %= sets errorLevel to 0 =%
:: initialise variables
set "paramC=0" & set "pFile=%tmp%\param.tmp"
:loop - the main loop
:: inc param counter and reset var storing nth param
set /a paramC+=1 & set "pN="
:: ECHO is turned on, %1 is expanded inside REM, GOTO jumps over REM,
:: and the output is redirected to param file
for %%A in (%%A) do (
setLocal disableExtensions
set prompt=#
echo on
for %%B in (%%B) do (
#goto skip
rem # %1 #
) %= for B =%
:skip - do not re-use this label
#echo off
endLocal
) >"%pFile%" %= for A =%
:: count lines in param file
for /f %%A in ('
find /c /v "" ^<"%pFile%"
') do if %%A neq 5 (
>&2 echo(multiline parameter values not supported & goto die
) %= if =%
:: extract and trim param value
for /f "useBack skip=3 delims=" %%A in ("%pFile%") do (
if not defined pN set "pN=%%A"
) %= for /f =%
set "pN=%pN:~7,-3%"
:: die if param value is " or "", else trim leading/trailing quotes
if defined pN (
setLocal enableDelayedExpansion
(call) %= OR emulation =%
if !pN!==^" (call;)
if !pN!=="" (call;)
if errorLevel 1 (
for /f delims^=^ eol^= %%A in ("!pN!") do (
endLocal & set "pN=%%~A"
) %= for /f =%
) else (
>&2 echo(empty parameter values (""^) not supported & goto die
) %= if errorLevel =%
) else (
:: no more params on cmd line
set /a paramC-=1 & goto last
) %= if defined =%
:: die if param value contains "
if not "%pN:"=""%"=="%pN:"=%" (
>&2 echo(quotes (^"^) in parameter values not supported & goto die
) %= if =%
:: assign nth param, shift params, and return to start of loop
set "param%paramC%=%pN%" & shift /1 & goto loop
:last - reached end of params
:: no param values on cmd line
if %paramC% equ 0 (
>&2 echo(no parameter values found & goto die
) %= if =%
:: list params
set param
goto end
:die
(call) %= sets errorLevel to 1 =%
:end
:: exit with appropriate errorLevel
endLocal & goto :EOF
The following conditions will terminate the program immediately:
no parameters found
multiline parameter
empty parameter (""", or " is permitted for the last parameter)
one or more quotes (") in a parameter value
To ease these restrictions, simply comment out the relevant lines. Read the inline comments for more information. Do not attempt to turn off the multiline parameter trap!
I invented the syntax-error-technic to solve the problem (partially).
With this solution it's even possible to receive multiline parameters and also carriage return characters.
There is no known parameter which fails!
BUT the drawback of this solution, the main process exits and only a child process continues.
That is a consequence of the capture trick, a syntax error is created by using an invalid parenthesis block ( Prepare ) PARAMS....
But the syntax error itself outputs the complete block, including the expanded value of %*.
The output is redirected to a file by the permanent redirect technic.
And the child process can retrieve the complete parameter from the file.
This solution can be useful, when the batch file only handles the parameter and always exit afterwards.
#echo off
REM *** Thread redirector
for /F "tokens=3 delims=:" %%F in ("%~0") do goto %%F
REM *** Clear params.tmp
break > params.tmp
start "" /b cmd /k "%~d0\:StayAlive:\..\%~pnx0 params.tmp"
(set LF=^
%=empty=%
)
REM *** Change prompt for better recognition
prompt #PROMPT#
REM *** Change streams permanently
REM *** stream1 redirects to params.tmp
REM *** stream2 redirects to nul
echo on >nul 2>nul 0>nul 3>params.tmp 4>nul 5>&3
#REM *** This is the magic part, it forces a syntax error, the error message itself shows the expanded %asterix without ANY modification
( Prepare ) PARAMS:%LF%%*%LF%
echo Works
exit /b
REM *** Second thread to fetch and show the parameters
:StayAlive
:__WaitForParams
if %~z1 EQU 0 (
goto :__WaitForParams
)
REM *** Show the result
findstr /n "^" %1
It's up to the user who types the command to escape any special characters. Your program cannot do anything about what the shell does before your program even runs. There is no other "bullet proof" solution to this.

Resources