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.