Use of findstr to seach regular expression in a batch file - batch-file

I would like to create a batch file to check if the file name has been written following certain rules.
The file name contains some parameters (letters and numbers) splitted by an hyphen character like :
FIN73-INV-2015-ANN
I would like to check the first two parameters (department name and document type) and above all check if the hypen has been written more than 1 time by mistake . e.g. FIN73--INV-2015-ANN
I have tried with the command FINDSTR but it seems that doesn't work because even if there are two hyphens the errorlevel is always 0 like in this case:
echo FIN73--INV-2015-ANN|FINDSTR /i "^[a-z]-[a-z]"
Do you have more suggestions ?
Thank you

for <StartOfString><letter><letter><letter><number><number><hyphen><letter>
use FINDSTR /i "^[a-z][a-z][a-z][0-9][0-9]-[a-z]"
If the count of letters/numbers are not known: FINDSTR /i "^[a-z]*[0-9]*-[a-z]"

#echo off
setlocal
rem Define valid departments and types, enclosed by slashes
set "departments=/FIN73/FIN83/"
set "types=/INV/INB/"
call :CheckFilename "FIN73-INV-ANN"
call :CheckFilename "FIN73--INV-2015-ANN"
call :CheckFilename "FIN93-INV-2015-ANN"
call :CheckFilename "FIN73-INX-2015-ANN"
call :CheckFilename "FIN73-INV-2015-ANN"
goto :EOF
:CheckFilename
setlocal EnableDelayedExpansion
set "filename=%~1"
echo/
echo Checking "%filename%"
rem Separate the filename in parts at hyphen
set i=0
for %%a in ("%fileName:-=" "%") do (
set /A i+=1
set "part[!i!]=%%~a"
)
if %i% lss 4 (
echo Error: missing parts
) else if %i% gtr 4 (
echo Error: duplicated hyphen
) else if "!departments:/%part[1]%/=!" equ "%departments%" (
echo Error: invalid department: %part[1]%
) else if "!types:/%part[2]%/=!" equ "%types%" (
echo Error: invalid type: %part[2]%
) else (
echo Name correct
)
exit /B
Output example:
Checking "FIN73-INV-ANN"
Error: missing parts
Checking "FIN73--INV-2015-ANN"
Error: duplicated hyphen
Checking "FIN93-INV-2015-ANN"
Error: invalid department: FIN93
Checking "FIN73-INX-2015-ANN"
Error: invalid type: INX
Checking "FIN73-INV-2015-ANN"
Name correct

Related

Command line - "x was unexpected at this time"

I was creating something with command line and came across this error - 'x was unexpected at this time', I tried switching the variable to x, %%x and %x% but none of them worked. Here are the code in the part:
echo Please enter the memory value [Recommended: 512M Xms 1024M Xmx]
set /p rams=Xms[MB]=
set /p ramx=Xmx[MB]=
echo %rams%|findstr /R "^[1-9][0-9]*$">nul
if NOT %errorlevel% EQU 0 (
echo Error: Please enter a valid number [Xms].
)
echo %ramx%|findstr /R "^[1-9][0-9]*$">nul
if NOT %errorlevel% EQU 0 (
echo Error: Please enter a valid number [Xmx].
)
if NOT %errorlevel% EQU 0 (
echo Execution failure: Invalid args
pause
goto start
)
set count=0
for %x in (*.jar) do set /a count+=1
if %count% == 1 (
java -Xms%rams%M -Xmx%ramx%M -jar BCU.jar
echo BCU.jar was executed.
pause
goto start
)
if %count% GEQ 1 (
echo [= = = = Multiple file found = = = =]
dir *.jar /B
echo [= = = = = = = = = = = = = = = = = =]
echo Please input file by the whole name with extension.
set /p file=Select File:
if exist %file% (
java -Xms%rams%M -Xmx%ramx%M -jar %file%
echo %file% was executed.
pause
) else (
echo File was not found.
pause
)
goto start
)
For some reason, the for command works normally when I execute it from the command prompt, but not when it was ran.
I ran the program as administrator
Only the for part is faulty, anything above it works normal
It is expected to have the count value defined and able to be used for the IF statement below
It is known that the java line works just fine without problem
For the full file it is located at: https://drive.google.com/file/d/1SgdoyCL5qaZGIWGJQDUKEJyEyUI2Y2u8/view?usp=sharing
Your immediate error is that you need to use %% within batch files, a single % works only on the command line. In other words, it should be:
for %%x in (*.jar) do set /a count+=1
(I'm not convinced you did try %%x as you state, since that actually works, but it may be that you just formatted it incorrectly).
But you'll strike another problem (at least) with using things like this (pared back and formatted for explanation):
if %count% GEQ 1 (
set /p file=Select File:
if exist %file% (
java -Xms%rams%M -Xmx%ramx%M -jar %file%
)
)
The entire block above is evaluated before it executes, meaning that %file% will be turned into whatever it was before the block started, despite the fact you're setting it within the block.
What you need to do is setlocal enabledelayedexpansion at the top of your file, and use the !file! delayed-expansion variant, something like:
setlocal enableextensions enabledelayedexpansion
:
if %count% GEQ 1 (
set /p file=Select File:
if exist !file%! (
java -Xms%rams%M -Xmx%ramx%M -jar !file!
)
)
:
endlocal
It's only needed for variables that can change within the loop, so not for the %rams% and %ramx% in this particular case - they're set before the if block starts.

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
)

Batch file to update a csv file on condition if file exists

Below is the Report.csv file, where I need to update the Status column based on the condition, I file in Lockbox exist. I have written the code for if file exist or not, but don't know how to update the Status column.
Lockbox Location Status Reason(N)
3080 Minot Y
1780 Minot N Not Scheduled
2280 Minot Y
3015 Windsor Y
2215 Windsor Y
1515 Windsor Y
29011 Windsor Y
Below is the code I have written. Please help..
#echo off
setlocal
set count=0
echo %time%
echo %date%
set y=%Date:~10,5%
set m=%Date:~4,2%
set d=%Date:~7,2%
if "%time:~0,1%"==" " (set tym=0%time:~1,1%) ELSE set tym=%time:~0,2%
set tm=%tym%%time:~2,0%
echo %tm%
pause
set pattern1=INGFINS.IMAGE.image1.29011.%y%%m%%d%%tm%
set pattern2=INGFINS.IMAGE.image2.2215.%y%%m%%d%%tm%
set pattern3=INGFINS.IMAGE.image3.1515.%y%%m%%d%%tm%
set pattern4=INGFINS.IMAGE.image4.3015.%y%%m%%d%%tm%
set pattern5=INGFINS.IMAGE.image5.1780.%y%%m%%d%%tm%
set pattern6=INGFINS.IMAGE.image6.2280.%y%%m%%d%%tm%
set pattern7=INGFINS.IMAGE.image7.3080.%y%%m%%d%%tm%
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern1%*.<" (
ECHO "%pattern1% exist"
) ELSE (
ECHO "%pattern1% not exist"
)
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern2%*.<" (
ECHO "%pattern2% exist"
) ELSE (
ECHO "%pattern2% not exist"
)
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern3%*.<" (
ECHO "%pattern3% exist"
) ELSE (
ECHO "%pattern3% not exist"
)
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern4%*.<" (
ECHO "%pattern4% exist"
) ELSE (
ECHO "%pattern4% not exist"
)
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern5%*.<" (
ECHO "%pattern5% exist"
) ELSE (
ECHO "%pattern5% not exist"
)
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern6%*.<" (
ECHO "%pattern6% exist"
) ELSE (
ECHO "%pattern6% not exist"
)
IF EXIST "C:\Users\Desktop\zipLocation\*%pattern7%*.<" (
ECHO "%pattern7% exist") ELSE (
ECHO "%pattern7% not exist"
)
rem call "statusReport.vbs"
endlocal
pause
Although some other users tend to close your question, I give next solution hoping that it could help. Commented .bat script:
#ECHO OFF >NUL
SETLOCAL EnableExtensions DisableDelayedExpansion
rem set input and output files: change to meet your circumstances
set "_fileIn=d:\bat\so\files\39587196_Report.TSV" tab-separated values
set "_fileOut=d:\bat\so\files\39587196_ReportOut.csv" comma-separated values
rem empty output file
>"%_fileOut%" type NUL
rem show input file
type "%_fileIn%"
echo(
rem obtain %_datetime% variable in locale independent yyyymmddHHMMSS format
for /f "tokens=2 delims==" %%G in (
'wmic OS get localdatetime /value') do set "_datetime=%%G"
set "_datetime=%_datetime:~0,14%"
rem reduce %_datetime% variable to yyyymmddHH format as the OQ does
set "_datetime=%_datetime:~0,10%"
set count=0
echo %date% %time%
echo %_datetime%
set "_pattern1=INGFINS.IMAGE.image1.29011.%_datetime%"
set "_pattern2=INGFINS.IMAGE.image2.2215.%_datetime%"
set "_pattern3=INGFINS.IMAGE.image3.1515.%_datetime%"
set "_pattern4=INGFINS.IMAGE.image4.3015.%_datetime%"
set "_pattern5=INGFINS.IMAGE.image5.1780.%_datetime%"
set "_pattern6=INGFINS.IMAGE.image6.2280.%_datetime%"
set "_pattern7=INGFINS.IMAGE.image7.3080.%_datetime%"
set "_pattern8=INGFINS.IMAGE.image7.8888.%_datetime%" for testing purposes
set "_ForGDelims= delims=," CSV input: delimiter is Comma (0x2C Comma)
set "_ForGDelims=" TSV input: delimiter is Tab (0x09 Character Tabulation)
if /I "%_fileIn:~-4%"==".tsv" set "_ForGDelims=" soft-code instead of above line
set "_csvHeader=" CSV header not processed yet
rem iterate input file, line by line, tokenized
for /F "usebackq tokens=1,2,3,*%_ForGDelims%" %%G in ("%_fileIn%") do (
set "_Lockbox=%%~G"
set "_Location=%%~H"
set "_Status=%%~I"
set "_Reason=%%~J"
if defined _csvHeader (
rem show checked Lockbox value (no NewLine)
<NUL set /P "=checking %%~G"
set "_LockboxFoundInVariable="
rem iterate all _pattern* variables
for /F "tokens=1,2 delims==" %%g in ('set _pattern') do (
rem take 4th token in %_pattern*% value
for /F "tokens=4 delims=." %%# in ("%%~h") do (
if "%%#"=="%%~G" (
echo - found %%G in %%g:%%h
set "_LockboxFoundInVariable=%%g"
rem ??? I don't comprehend what this ↓ LessThan should mean ???
IF EXIST "C:\Users\Desktop\zipLocation\*%%h*.<" (
rem ??? I don't comprehend what this ↑ LessThan should mean ???
set "_Status=y" pattern exists
) ELSE (
set "_Status=n" pattern does not exist
)
)
)
)
rem echo NewLine if necessary i.e. if checked Lockbox value not in any _pattern*
if not defined _LockboxFoundInVariable (echo()
) else (
set "_csvHeader=anything" CSV header processed already
)
call :csvLineOut
)
rem show output file
echo(
type "%_fileOut%"
ENDLOCAL
goto :eof
:csvLineOut
rem you could use <TAB>s ↓ ↓ ↓ instead of commas
>>"%_fileOut%" echo %_Lockbox%,%_Location%,%_Status%,"%_Reason%"
goto :eof
Please read Delimiter-separated values about .csv vs. .tsv difference. Note that I don't keep using quotation marks to surround every field strictly…
Output:
==> D:\bat\SO\39587196.bat
Lockbox Located Status Reason(N)
3080 Minot Y
1111 Test Y
1780 Minot N Not Scheduled
2280 Minot Y
2215 Windsor Y
29011 Windsor Y
9999 Test Y
20.09.2016 17:41:53,87
2016092017
checking 3080 - found 3080 in _pattern7:INGFINS.IMAGE.image7.3080.2016092017
checking 1111
checking 1780 - found 1780 in _pattern5:INGFINS.IMAGE.image5.1780.2016092017
checking 2280 - found 2280 in _pattern6:INGFINS.IMAGE.image6.2280.2016092017
checking 2215 - found 2215 in _pattern2:INGFINS.IMAGE.image2.2215.2016092017
checking 29011 - found 29011 in _pattern1:INGFINS.IMAGE.image1.29011.2016092017
checking 9999
Lockbox,Located,Status,"Reason(N)"
3080,Minot,n,""
1111,Test,Y,""
1780,Minot,n,"Not Scheduled"
2280,Minot,n,""
2215,Windsor,n,""
29011,Windsor,n,""
9999,Test,Y,""
==>
Further resources (required reading, incomplete):
(command reference) An A-Z Index of the Windows CMD command line
(helpful particularities) Windows CMD Shell Command Line Syntax
(%~G, %~H etc. special page) Command Line arguments (Parameters)
(special page) EnableDelayedExpansion
(>, >> etc. special page) Redirection
(%_fileIn:~-4% etc.) Extract part of a variable (substring)
(%% doubled percent sign) Escape Characters, Delimiters and Quotes

Batch: How to remove all empty columns from a csv file

I have a CSV file like this:
P,PC,,PL,B,15feb16,P,Bay,RP,15-FEB-16,22-FEB-16,7,,,,,,11,14,138,14,16,993.42,-12,-84,-12,,,,,,,,,17,2,-10,0,0,1,1,16:05:53,15FEB16
P,PC,,PL,I,1FEB-16,P,In,RP,15-FEB-16,22-FEB-16,7,,,,,,25,5,32,5,5,-29.7,-24,-168,-24,,,,,,,,,520,14,-10,0,0,1,1,10-MAY-201606:05:53,15-FEB-16
P,PC,,PC,S,15FEB16,P,Su,RP,15-FEB-16,22-FEB-16,7,,,,,,6,5,32,56,5,4.65,0,0,0,,,,,,,,,546,0,0,0,0,1,1,10-MAY-201606:05:53,15-FEB-16
The code I have written is:
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%a in (C:\Pca.csv) do (
set line=%%a
set line=!line:,,=, ,!
set line=!line:,,=, ,!
for /F "tokens=1,2,3* delims=," %%i in (^"!line!^") do (
echo %%i,%%l>>C:\P.csv
)
)
But it only deletes 2nd and 3rd column, no matter whether it is empty or contains data.
The sample output file should be like:
P,PC,PL,B,15feb16,P,Bay,RP,15-FEB-16,22-FEB-16,7,11,14,138,14,16,993.42,-12,-84,-12,17,2,-10,0,0,1,1,16:05:53,15FEB16
P,PC,PL,I,1FEB-16,P,In,RP,15-FEB-16,22-FEB-16,7,25,5,32,5,5,-29.7,-24,-168,-24,520,14,-10,0,0,1,1,10-MAY-201606:05:53,15-FEB-16
P,PC,PC,S,15FEB16,P,Su,RP,15-FEB-16,22-FEB-16,7,6,5,32,56,5,4.65,0,0,0,546,0,0,0,0,1,1,10-MAY-201606:05:53,15-FEB-16
Here is a quite comprehensive and adaptive script that removes empty columns from CSV-formatted data.
Before the code is shown, let us take a look at the help message that appears when called with /?:
"del-empty-cols-from-csv.bat"
This script removes any empty columns from CSV-formatted data. A column is con-
sidered as empty if the related fields in all rows are empty, unless the switch
/H is given, in which case the first line (so the header) is evaluated only.
Notice that fields containing white-spaces only are not considered as empty.
USAGE:
del-empty-cols-from-csv.bat [/?] [/H] csv_in [csv_out]
/? displays this help message;
/H specifies to regard the header only, that is the very first row,
to determine which columns are considered as empty; if NOT given,
the whole data, hence all rows, are taken into account instead;
csv_in CSV data file to process, that is, to remove empty columns of;
these data must be correctly formatted CSV data, using the comma as
separator and the quotation mark as text delimiter; regard that
literal quotation marks must be doubled; there are some additional
restrictions: the data must not contain any line-breaks; neither
must they contain any asterisks nor question marks;
csv_out CSV data file to write the return data to; this must not be equal
to csv_in; note that an already existing file will be overwritten
without prompt; if not given, the data is displayed on the console;
As you can read, there are two operation modes: standard (no switch) and header mode (switch /H).
Given that the following CSV data is fed into the script...:
A, ,C, ,E,F
1, , ,4,5,
1, , , ,5,
1, ,3,4, ,
...the returned CSV data in standard mode will look like...:
A,C, ,E,F
1, ,4,5,
1, , ,5,
1,3,4, ,
...and the returned CSV data in header mode (/H) will look like:
A,C,E,F
1, ,5,
1, ,5,
1,3, ,
Remind that the spaces in the above sample data must actually not be present in the files; they have just been inserted here for better illustration of the said operation modes.
Now, this is the complete code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "OPT_HEAD=%~1"
if "%OPT_HEAD%"=="/?" (
goto :MSG_HELP
) else if /I "%OPT_HEAD%"=="/H" (
shift
) else if "%OPT_HEAD:~,1%"=="/" (
set "OPT_HEAD="
shift
) else set "OPT_HEAD="
set "CSV_IN=%~1"
if not defined CSV_IN (
>&2 echo ERROR: no input file specified!
exit /B 1
)
set "CSV_OUT=%~2"
if not defined CSV_OUT set "CSV_OUT=con"
for /F "delims==" %%V in ('2^> nul set CELL[') do set "%%V="
setlocal EnableDelayedExpansion
if not defined OPT_HEAD (
for /F %%C in ('^< "!CSV_IN!" find /C /V ""') do set "NUM=%%C"
) else set /A NUM=1
set /A LIMIT=0
< "!CSV_IN!" (
for /L %%L in (1,1,%NUM%) do (
set /P "LINE="
call :PROCESS LINE LINE || exit /B !ErrorLevel!
set /A COUNT=0
for %%C in (!LINE!) do (
set /A COUNT+=1
if not defined CELL[!COUNT!] set "CELL[!COUNT!]=%%~C"
if !LIMIT! LSS !COUNT! set /A LIMIT=COUNT
)
)
)
set "PAD=" & for /L %%I in (2,1,!LIMIT!) do set "PAD=!PAD!,"
> "!CSV_OUT!" (
for /F usebackq^ delims^=^ eol^= %%L in ("!CSV_IN!") do (
setlocal DisableDelayedExpansion
set "LINE=%%L%PAD%"
set "ROW="
set /A COUNT=0
setlocal EnableDelayedExpansion
call :PROCESS LINE LINE || exit /B !ErrorLevel!
for %%C in (!LINE!) do (
endlocal
set "CELL=%%C"
set /A COUNT+=1
setlocal EnableDelayedExpansion
if !COUNT! LEQ !LIMIT! (
if defined CELL[!COUNT!] (
for /F delims^=^ eol^= %%R in ("!ROW!,!CELL!") do (
endlocal
set "ROW=%%R"
)
) else (
endlocal
)
) else (
endlocal
)
setlocal EnableDelayedExpansion
)
if defined ROW set "ROW=!ROW:~1!"
call :RESTORE ROW ROW || exit /B !ErrorLevel!
echo(!ROW!
endlocal
endlocal
)
)
endlocal
endlocal
exit /B
:PROCESS var_return var_string
set "STRING=!%~2!"
if defined STRING (
set "STRING="!STRING:,=","!""
if not "!STRING!"=="!STRING:**=!" goto :ERR_CHAR
if not "!STRING!"=="!STRING:*?=!" goto :ERR_CHAR
)
set "%~1=!STRING!"
exit /B
:RESTORE var_return var_string
set "STRING=!%~2!"
if "!STRING:~,1!"==^""" set "STRING=!STRING:~1!"
if "!STRING:~-1!"==""^" set "STRING=!STRING:~,-1!"
if defined STRING (
set "STRING=!STRING:","=,!"
)
set "%~1=!STRING!"
exit /B
:ERR_CHAR
endlocal
>&2 echo ERROR: `*` and `?` are not allowed!
exit /B 1
:MSG_HELP
echo(
echo("%~nx0"
echo(
echo(This script removes any empty columns from CSV-formatted data. A column is con-
echo(sidered as empty if the related fields in all rows are empty, unless the switch
echo(/H is given, in which case the first line ^(so the header^) is evaluated only.
echo(Notice that fields containing white-spaces only are not considered as empty.
echo(
echo(
echo(USAGE:
echo(
echo( %~nx0 [/?] [/H] csv_in [csv_out]
echo(
echo( /? displays this help message;
echo( /H specifies to regard the header only, that is the very first row,
echo( to determine which columns are considered as empty; if NOT given,
echo( the whole data, hence all rows, are taken into account instead;
echo( csv_in CSV data file to process, that is, to remove empty columns of;
echo( these data must be correctly formatted CSV data, using the comma as
echo( separator and the quotation mark as text delimiter; regard that
echo( literal quotation marks must be doubled; there are some additional
echo( restrictions: the data must not contain any line-breaks; neither
echo( must they contain any asterisks nor question marks;
echo( csv_out CSV data file to write the return data to; this must not be equal
echo( to csv_in; note that an already existing file will be overwritten
echo( without prompt; if not given, the data is displayed on the console;
echo(
exit /B
assuming, your original csv looks like this:
id_users,,,quantity,,date
1,,,1,,2013
1,,,1,,2013
2,,,1,,2013
then this single line should solve your request:
(for /f "tokens=1-3 delims=," %%a in (c:\pca.csv) do echo %%a,%%b,%%c)>c:\p.csv
resulting in:
id_users,quantity,date
1,1,2013
1,1,2013
2,1,2013
The trick is: consecutive delimiters are treated as one.
Edit: another approach, as it turned out, there are much more colums, than the original question showed.
#echo off
break>out.txt
for /F "delims=" %%a in (c:\pca.csv) do call :shorten "%%a"
goto :eof
:shorten
set "line=%~1"
:remove
set "line=%line:,,=,%"
echo %line%|find ",,">nul && goto :remove
echo %line%>>c:\p.csv
break>c:\p.csv: create outputfile (overwrite if exist)
replace two consecutive commas with one;
repeat, if there are any more consecutive commas.
Write the resulting line to the outfile.

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