Create a function from CMD (Without batch file) - batch-file

In batch file, to define a function you put a : sign and then the name of the function
For example:
:func
copy "test.txt" "D:\test"
Then when you want to call the function you use goto:
goto func
Is there a way to do that without a batch file? To define a function from the cmd?

You could make a macro.
set "func=copy "test.txt" "D:\test""
Then to execute it,
%func%

Batch (cmd.exe) doesn't knows functions at all.
With labels you can build a construct that works similar to functions and for returning use exit /b ( or goto :eof),
but in reality this isn't a function.
But labels can only be used inside of batch files!
At the other side you could use macros, these are variables with code.
Also no real functions, but can work like one.
Usage of a macro (in this case to get the string length):
set "var=abcdef"
%strlen% var
And to build the macro you only need to copy this into your command prompt
cmd /v:on
set LF=^
::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:::: StrLen pString
set strLen=#for /L %n in (1 1 2) do #if %n==2 (%\n%
for /F "tokens=1,2 delims=, " %1 in ("!argv!") do #(%\n%
set "str=A^!%~1^!"%\n%
set "len=0"%\n%
for /l %A in (12,-1,0) do #(%\n%
set /a "len|=1<<%A" ^> nul %\n%
for %B in (^^!len^^!) do #if "^!str:~%B,1^!"=="" set /a "len&=~1<<%A" ^> nul%\n%
)%\n%
for %v in (^^!len^^!) do #if "%~2" neq "" (set "%~2=%v") else echo %v%\n%
) %\n%
) ELSE set argv=,
But macros have a tendency to become nasty.
Or you could read DosTips: Executing GOTO/CALL in a cmd.exe, it's a little bit advanced technic

:func is not a function but label in batch file. I.e. point for goto. There is no return like in subroutine/function.

Related

Is there a way to create a CMD command with arguments?

I'm trying to create a command in the command prompt, but use arguments like you can do with a function in another language.
For example, you can create a batch file in the C:\Windows\System32 folder and execute it in the command prompt by typing its name, creating a "command." This is very simple, though, and simply runs whatever the batch file contains.
My command I made is called toolbx, which has different useful shortcuts in the command prompt. But, I want to execute different tools by typing something like toolbx 1. Here's what it might look like.
toolbx [input received changing what tool you want to use] (e.g. 1)
In toolbx.bat
if %inputRecieved% == 1 goto 1
I'm not the best at explaining, so another example in Python might be:
def functionWithArgs (tool)
Executed as:
functionWithArgs(1)
As another summary:
I'm trying to make a command used in the command prompt with a batch file
I want the batch file to have arguments defined when the command is run (like the python example) that can be used as variables in the batch file.
Thank you in advance!
It is your lucky day today, windows batch files do in fact support a feature called command-line parameters. Here is what you need to do:
set inputRecieved=%1
if "%inputRecieved%"=="1" goto label1
goto :eof
:label1
blabla...
Have fun coding!
P.S.
Please note that the correct spelling of the word is "received", not "recieved".
You can use the macro style invented by #jeb, #dbenham and DosTips user #Ed Dyreen:
Definitions:
#echo off
SETLOCAL DISABLEDELAYEDEXPANSION
::Definitions
( set LF=^
%= EMPTY =%
)
set ^"NL=^^^%LF%%LF%^%LF%%LF%^^"
The whole purpose is to create a multiline variable. See the batch newline variable hack
Main Macro:
::Macro
ENDLOCAL &^
set $MACRO.Toolbx=FOR %%a in (args main) do if "%%a" == "main" (%NL%
for %%A in (%payload%) do (%NL%
%= MAIN MACRO HERE, END LINES WITH NL =%%NL%
)%NL%
) ELSE SETLOCAL ENABLEDELAYEDEXPANSION ^& set args=,
exit /b
Save this as MACRO.Toolbx.bat. To use the macro inside your batch file:
call MACRO.Toolbx.bat
%$MACRO.Toolbx% COMMAND_LINE_ARGS
The macro uses a clever FOR loop to "catch" the arguments. Also, in order to catch arguments, DISABLEDELAYEDEXPANSION is necessary, which is inseparable from SETLOCAL. However, ENDLOCAL will destroy all user-defined variable in that range, but we need the newline definitions. Therefore, ENDLOCAL and the macro definition must be on the same line. This technique is called ENDLOCAL tunneling.
sure
[Complete path and file name] [Parameter1] [Parameter2] [and_so_on]
Is a space that separates parameters that can be expanded with %1 %2 %3...
You can expand all parameters with %* or up to 9 parameters individually
for example:
%windir%\system32\toolbx.cmd Tool_1
note the parameter beign passed is: Tool_1
in the example below is beign used at line 2 where %1 expands: Tool_1
setlocal EnableDelayedExpansion
set tool=%1
set argument_1=Have
set argument_2=a
set argument_3=Nice
set argument_4=Day
CALL :Function_%tool% argument_1 argument_2 argument_3 argument_4
:: after Function_Tool_1 ends by reading the next GOTO :EOF after its Label,
:: will return to read instructions after this line
::
pause&&exit
::
:Function_Tool_1
echo. %argument_1% %argument_2% %argument_3% %argument_4%
goto :EOF
::
::
NOTE:
When using CALL to call labels, you have to use a colon for example:
CALL :YourLabel
And after the label all the parameters or arguments separated with a space
for example:
CALL :YourLabel param1 param2 param3 parampampam etc
I think the syntax for labels is the same as for variables.
You have to use a GOTO :EOF to end a function or procedure
See you don't use a colon when going to the label using GOTO for example:
GOTO YourLabel
but when using CALL is different, that is explained better typing:
EXIT /?
GOTO /?
& is used to separate instructions
&& is used to separate instructions inside a batch file as toolbx.cmd
Also note in the example above:
%1 %2 %3 and %4 along the function could expand the variable name, for example:
echo. %1 %2 %3 %4
would echo: argument_1 argument_2 argument_3 argument_4
and using SETLOCAL EnableDelayedExpansion
!%1! could expand the value of the variable, for example:
echo. !%1! !%2! !%3! !%4!
would echo: Have a Nice Day
A technique has been developed using for loops that allows arguments to be captured when a variable is expanded - Batch Macros.
An outer for loop is used, which on the first iteration assigns a variable containing the argument values, and the inner loop/s proccess and act upon those values.
#echo off
Set "Macroname=For %%n in (1 2)Do if %%n==2 (Echo(!Args!)Else Set args="
Setlocal EnableDelayedExpansion
%Macroname% Hello World
The technique requires defining the macro variable in an environment where Delayed expansion is disabled, then using Setlocal EnableDelayedExpansion to enable the macro. ( Unless of course all ! expansion symbols are correctly escaped during macro definition for an environment where delayed expansion is enabled. )
Below is a template for batch macro's that builds an Args array and and proccesses switches that trail arguments. The purpose of the template is to allow quick scripting of advanced functions by having all the argument and switch handling logic ready to go, so that the meat of the function is all that needs to be scripted.
Args are returned to the array $Macroname_args[!$Macroname_args[i]!] where:
!$Macroname_args[i]! is a 0 indexed count of args.
Switches are returned in associated variables using the switch string as the index IE:
%MacroName% /A
Results in: !$Macroname_Switch[A]!==true
Or if a subarg is supplied:
%MacroName% /A sub arg
Results in: !$Macroname_Switch[A]!==sub arg
The macro uses Substring modification to seperate switches from args and process switches.
As such, arg or switch values containing the following characters:
= * / "
should be supplied in a substituted form:
{EQ} {AS} {FS} {DQ}, which are replaced using substitution built into the macro.
#Echo off
(Set \n=^^^
%= \n macro newline variable. Do not modify =%)
(Set LF=^
%= LF newline variable. Do not modify =%)
:# Example Switches for demonstrating concept. List used twice within macro switch handling. Define outside macro to
:# simplify maintainance.
Set $MacroName_Switches="A" "B" "C" "D" "E" "Def"
Set $MacroName=For %%n in (1 2)Do if %%n==2 (%\n%
For /F "Tokens=1,2 Delims==" %%G in ('Set "$Macroname_Arg[" 2^^^> nul')Do Set "%%~G="%\n%
For %%G in ( %$MacroName_Switches% )Do Set "$MacroName_Switch[%%~G]="%\n%
If not "!$MacroName_args:* /=!" == "!$MacroName_args!" (%\n: build $MacroName.Args[!$MacroName_arg[i]!] array if args present =%
Set "$MacroName_leading.args=!$MacroName_args:*/=!"%\n%
For /F "Delims=" %%G in ("!$MacroName_leading.args!")Do Set "$MacroName_leading.args=!$MacroName_args:/%%G=!"%\n%
Set ^"$MacroName_args=!$MacroName_args:"=!"%\n%
Set "$MacroName_arg[i]=0"%\n%
For %%G in (!$MacroName_leading.args!)Do (%\n%
Set /A "$MacroName_arg[i]+=1"%\n%
Set "$MacroName_arg[!$MacroName_arg[i]!]=%%~G"%\n%
For %%i in ("!$MacroName_arg[i]!")Do (%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{FS}=/!"%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{AS}=*!"%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{EQ}==!"%\n%
Set ^"$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{DQ}="!"%\n%
)%\n%
)%\n%
) Else (%\n: remove doublequotes from args before switch processing switches =%
Set ^"$MacroName_args=!$MacroName_args:"=!"%\n%
)%\n%
Set "$MacroName_LastSwitch="%\n%
For /L %%L in (2 1 4)Do If "!$MacroName_LastSwitch!" == "" (%\n%
If "!$MacroName_Args:~-%%L,1!" == " " Set "$MacroName_LastSwitch=_"%\n%
If "!$MacroName_Args:~-%%L,1!" == "/" (%\n: Flag last switch true if no subargs; Works for character switches of up to 3 characters =%
For /F "Delims=" %%v in ('Set /A "%%L-1"')Do Set "$MacroName_Switch[!$MacroName_Args:~-%%v!]=true"%\n%
If not "!$MacroName_Args:/?=!." == "!$MacroName_Args!." Set "$MacroName_Switch[help]=true"%\n%
Set "$MacroName_Args=!$MacroName_Args:~0,-%%L!"%\n%
Set "$MacroName_LastSwitch=_"%\n%
)%\n%
)%\n%
For %%G in ( %$MacroName_Switches% )Do If not "!$MacroName_args:/%%~G =!" == "!$MacroName_args!" (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Args:*/%%~G =!"%\n%
If not "!$MacroName_Switch[%%~G]:*/=!" == "!$MacroName_Switch[%%~G]!" (%\n%
Set "$MacroName_Trail[%%~G]=!$MacroName_Switch[%%~G]:*/=!"%\n%
For %%v in ("!$MacroName_Trail[%%~G]!")Do (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]: /%%~v=!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:/%%~v=!"%\n%
)%\n%
Set "$MacroName_Trail[%%~G]="%\n%
If "!$MacroName_Switch[%%~G]:~-1!" == " " Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:~0,-1!"%\n%
If "!$MacroName_Switch[%%~G]!" == "" Set "$MacroName_Switch[%%~G]=true"%\n%
If not "!$MacroName_Switch[%%~G]!" == "" If not "!$MacroName_Switch[%%~G]!" == "true" (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{FS}=/!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{AS}=*!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{EQ}==!"%\n%
Set ^"$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{DQ}="!"%\n%
)%\n%
)%\n%
)%\n: Insert Switch Assessment Below. Use conditional testing of valid switches to enact macro functions before $MacroName_args assignment =%
REM INSERT COMMANDS BETWEEN THE ABOVE AND BELOW LINES TERMINATING EACH WITH \n VARIABLE %\n%
%= EXAMPLE. Remove this line. =% Echo(!LF!Example: %%$Macroname%%!$Macroname_args!%\n%
%= EXAMPLE. Remove this line. =% Set $Macroname_Arg[%\n%
%= EXAMPLE. Remove this line. =% Set $Macroname_Switch[%\n%
%= ESCAPE AMPERSANDS AND REDIRECTION CHARACTERS DURING MACRO DEFINITION. =%) Else Set $MacroName_args=
Setlocal enableDelayedExpansion
:# Proof of concept
%$Macroname% "{AS} param 1 {EQ} {DQ}https:{FS}{FS}stackoverflow.com{FS}posts{FS}60793679{DQ}" "param 2" /C one /D 12 /Def
%$Macroname% /B a b /E /Def "hello world & ^! > < | %%" /A 1 + 2 {EQ} 3 /Undefined switch

Changing a flag in the ini file from batch script

I have a script which will check if the file names and the content of the files are same or not, below is the code and it is working fine
ECHO OFF
CLS
for %%i in (C:\Users\f1ym41a\Documents\deep\*.DAT) do (
fc C:\Users\f1ym41a\Documents\deep\MOVE.DAT %%i > NUL
if errorlevel 1 (
CALL :error
echo C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are different >>output.log
) ELSE (
CALL :next
echo C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are same >>output.log
)
timeout 5
)
PAUSE
What i need to do is if the file names are same then it will change the flag in the ini file to 1. Below is the ini file (deep.ini)
[INI]
flag = 0
Since i am new to batch scripting. Can somebody help me out with this?
You can try with replacer.bat:
call replacer.bat move.dat "flag = 0" "flag = 1"
This is an easy to achieve task with using JREPL.BAT written by Dave Benham which is a batch file / JScript hybrid to run a regular expression replace on a file using JScript.
#echo off
if not exist "%USERPROFILE%\Documents\deep\MOVE.DAT" goto :EOF
if not exist "%~dp0jrepl.bat" goto :EOF
call "%~dp0jrepl.bat" "^(flag *= *)0" "$11" /F "%USERPROFILE%\Documents\deep\MOVE.DAT" /O -
The batch file first checks if the file to modify exists at all and immediately exits if this condition is not true, see Where does GOTO :EOF return to?
The batch file JREPL.BAT must be stored in same directory as the batch file with the code above. For that reason the batch file checks next if JREPL.BAT really exists in directory of the batch file and exits if this condition is not true.
The meaning of the regular expression search string is:
^ ... find at beginning of a line
(...) ... a string referenced in replace string with $1 to keep this part of found string unmodified
flag ... case-sensitive the string flag
 * ... with 0 or more spaces
= ... and an equal sign
 * ... and once more 0 or more spaces
0 ... and the character 0.
The replace string back-references the found string between beginning of line and character 0 with $1 and replaces 0 by 1.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains also %~dp0 ... drive and path of argument 0 which is the batch file path always ending with a backslash.
echo /?
goto /?
if /?
jrepl.bat /?
:: 1st need remove some possible space in the string to got more precision
when compare them: "flag = 0" will became "flag=0", no
space and no tab.
:: 2nd for to do this, use this "' (2 characters) to set a variable that
use "=" to string instead a special character,
by set "'=flag=0" (very old technical!)
:: 3rd treat equal, treat tab character, and to remove it, because some time
this is a invisible and possible character that can eventually occur
in file dat, see in this question 10878138
:: 4th Compare the strings by string from file by file, line by line...
:: finely You need replace line in the file (.dat or .ini) this part I´m really confuse, but the code are above, sorry if my error!
Obs: use the conversion of this "flag = 0" this this one "flag=0", only for processing comparatives operation, wend the %%i match flag = 0 then only changed to replace to files by flag = 1, bat, a specific thing here is the command fc are comparing %%i, by the same file in looping for with no other file.
#echo off && setlocal EnableExtensions EnableDelayedExpansion
set "'=flag=0"
set _file_new_flag1="%temp%\Flag1.dat"
set _path_to_dats=C:\Users\f1ym41a\Documents\deep\
for /f "delims= " %%T in ('forFiles /p "." /m "%~nx0" /c "cmd /c echo(0x09"') do set "_tab=%%T"
type nul >output.log && set "_tab=%_tab:~0,1%"
cd /d "!_path_to_dats!"
for /f "tokens=* delims= " %%x in ('dir /o-d /on /b "*.dat"') do (
if defined _file_new_flag (
move /y "!_file_new_flag1!" "!_file_now!"
set _file_now=<nul
set "_file_now=%%~x"
) else (
set "_file_now=%%~x"
)
call :_file_compare_:
)
endlocal & goto :_end_of_file_:
:_file_compare_:
for /f "tokens=* delims= " %%X in ('type "!_file_now!"') do (
for /f "tokens=* delims= " %%i in ('echo/"%%~X"') do (
set "_to_compare=%%~i"
call set "_to_compare=!_to_compare: =!"
for /f "tokens=* delims=" %%I in ('echo/%_tab%') do call set "_to_compare=!_to_compare:%%I=!"
if ["!_to_compare!"] equ ["%'%"] (
echo/C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are same >>output.log
echo/%%~i>>!_file_new_flag1!
) else (
echo/C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are different >>output.log
echo/flag = 1>>!_file_new_flag1!
)
timeout /t 5
set _to_compare=<nul
)
)
exit /b
:_end_of_file_:
So sorry about my English.

Batch read file that contains greater / lower than signs / carets

Alright, so I'm trying to read all lines from a text file. My current way is:
FOR /F "delims=0123456789 tokens=1,*" %%F IN ('find /v /n "" ^< myFile.bat') DO (
SET line = %%G
:: ^ Syntax errors at this line
SET line=!line:~1!
:: Yes, I have delayed expansions enabled due to a lot of fors and ifs needed
)
Basically the input file is another batch file which also contains the exact same code as above and other code with <, >, ^ etc. Once I read a line, it's basically impossible to use %%G as it will expand to stuff like:
SET line=ECHO Hello >> someFile
or
SET line=FOR /L %%G IN (1,1,5) ( SET "line=ECHO Hello %%G" & call :something & >nul SET /P =. )
Which will obviously not work. I've tried many workarounds (all have failed), including:
SET line="%%G
Which (most of the time) works, but from there using is with basically anything is near-impossible, even with something like:
:fixLine
SET line=%line:^=^^^^%
SET line=%line:<=^^^<%
SET line=%line:>=^^^>%
SET line=%line:'=^^^'%
SET line=%line:~2%
GOTO :returnFixLine
But all methods fail in some case or another. How can I read a file containing a batch script from another batch script, including special characters?
EDIT:
Doing
SET "line=%%G"
won't work, as %%G can contain quotes, and even if it doesn't, carets are still special in quotes:
SET "line=ECHO ^<Hello^>"
will turn into
SET "line=ECHO <Hello>"
Also, lines containing exclamation marks will get expanded too.
The first problems are the spaces in set line = %%G, as you set the variable line<space> instead of line.
And you prefix to the content a space.
You should use set line=%%G instead, but even that produces sometimes problems, when spaces are behind the %%G they are appended.
The best way is to use the extended SET syntax set "line=%%G".
Btw. There exists only one special charcter which can fail with a FOR-parameter expansion, that is the exclamation mark when delayed expansion is enabled.
The solution is to toggle delayed expansion.
setlocal DisableDelayedExpansion
FOR /F "delims= tokens=*" %%F IN ('find /v /n "" ^< myFile.bat') DO (
SET "line=%%F"
setlocal EnableDelayedExpansion
SET "line=!line:*]=!"
echo(Testoutput: !line!
endlocal
)

What does %\n% mean - Batch file

I am trying to understand a script but I dont know how %\n% is used, I dont understand why it can stand behind a string -->
set getKey=%\n%
for %%# in (1 2) do if %%#==2 (%\n%
set key=%\n%
set inKey=%\n%
set keyTest=%\n%
^<^&9 set /p "inKey="%\n%
if defined inKey (%\n%
set inKey=!inKey:~0,-1!%\n%
for %%C in (!args!) do set /a keyTest=1^&if /i !inKey! equ %%~C set key=!inKey!%\n%
)%\n%
if not defined keyTest set key=!inKey!%\n%
set draw=%\n%
cls%\n%
for /l %%Y in (0,1,%height%) do echo(!line%%Y!%\n%
echo Speed = !Difficulty! !replay!%\n%
echo Time = !m!:!s!%\n%`
...(it goes further)
what is cls%\n% ??
if somebody understands me, please help me
THNX
the whole script -->
http://www.dostips.com/forum/viewtopic.php?f=3&t=4741
%\n% is the expansion of the environment variable \n. So you need to look where that is defined.
When you find it there is a comment explaining it:
:: define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
What is LF?
:: define LF as a Line Feed (newline) character
set ^"LF=^
^" Above empty line is required - do not remove
So LF is being set to a newline character. All that gubbins is just a long-winded way of doing something which is not easy to do in a CMD batch file.
%\n% is a normal environment variable.
Run this to see it work:
#echo off
set \n=a
set getKey=%\n%
echo "%getkey%"
pause

Parsing of cmdline arguments containing =

After using batch files for many years I was surprised to discover that the equals sign '=' is considered an argument separator.
Given this test script:
echo arg1: %1
echo arg2: %2
echo arg3: %3
and an invocation:
test.bat a=b c
the output is:
arg1: a
arg2: b
arg3: c
Why is that and how can it be avoided? I don't want the user of the script to account for this quirk and quote "a=b", which is counter-intuitive.
This batch script was run on Windows 7.
===== EDIT =====
A little more background: I encountered this problem when writing a bat file to start a Java application. I wanted to consume some args in the bat file and then pass the rest to the java application. So my first attempt was to do shift and then rebuild the args list (since %* is not affected by shift). It looked something like this, and that's when I discovered the issue:
rem Rebuild the args, %* does not work after shift
:args
if not "%1" == "" (
set ARGS=!ARGS! %1
shift
goto args
)
The next step was to not use shift anymore, but rather implement shift by hand by removing one character at a time from %* until a space is encountered:
rem Remove the 1st arg if it was the profile
set ARGS=%*
if not "%FIRST_ARG%" == "%KNOA_PROFILE%" goto remove_first_done
:remove_first
if not defined ARGS goto remove_first_done
if "%ARGS:~0,1%" == " " goto remove_first_done
set ARGS=%ARGS:~1%
goto remove_first
:remove_first_done
But this is ugly and might still fail in some cases I haven't considered. So finally I decided to write a Java program to deal with the argument parsing! In my case this is fine, since I am launching a server and the penalty of an extra java invocation is minimal. It's mind-boggling what you end up doing sometimes.
You might wonder why didn't I take care of the args in the Java application itself? The answer is that I want to be able to pass JVM options like -Xmx which must be processed before invoking java.
I'm guessing it does this so that /param=data is the same as /param data
I don't know any tricks to fix that stupid (probably by design) parsing issue but I was able to come up with a super ugly workaround:
#echo off
setlocal ENABLEEXTENSIONS
set param=1
:fixnextparam
set p=
((echo "%~1"|find " ")>nul)||(
call :fixparam %param% "%~1" "%~2" %* 2>nul
)
if "%p%"=="" (set "p%param%=%1") else shift
shift&set /A param=%param% + 1
if not "%~1"=="" goto fixnextparam
echo.1=%p1%
echo.2=%p2%
echo.3=%p3%
echo.4=%p4%
echo.5=%p5%
goto:EOF
:fixparam
set p%1=
for /F "tokens=4" %%A in ("%*") do (
if "%%~A"=="%~2=%~3" set p=!&set "p%1=%%A"
)
goto:EOF
When I execute test.cmd foo=bar baz "fizz buzz" w00t I get:
1=foo=bar
2=baz
3="fizz buzz"
4=w00t
5=
The problem with this is of course that you cannot do %~dp1 style variable expansion.
It is not possible to do call :mylabel %* and then use %1 either because call :batchlabel has the same parameter parsing problem!
If you really need %~dp1 handling you could use the WSH/batch hybrid hack:
#if (1==1) #if(1==0) #ELSE
#echo off
#SETLOCAL ENABLEEXTENSIONS
if "%SPECIALPARSE%"=="*%~f0" (
echo.1=%~1
echo.2=%~2
echo.3=%~3
echo.4=%~4
echo.5=%~5
) else (
set "SPECIALPARSE=*%~f0"
cscript //E:JScript //nologo "%~f0" %*
)
#goto :EOF
#end #ELSE
w=WScript,wa=w.Arguments,al=wa.length,Sh=w.CreateObject("WScript.Shell"),p="";
for(i=0;i<al;++i)p+="\""+wa.Item(i)+"\" ";
function PipeStream(i,o){for(;!i.AtEndOfStream;)o.Write(i.Read(1))}
function Exec(cmd,e){
try{
e=Sh.Exec(cmd);
while(e.Status==0){
w.Sleep(99);
PipeStream(e.StdOut,w.StdOut);
PipeStream(e.StdErr,w.StdErr);
}
return e.ExitCode;
}catch(e){return e.number;}
}
w.Quit(Exec("\""+WScript.ScriptFullName+"\" "+p));
#end
I am only answering this: >> Why is that and how can it be avoided?
My suggestion: To use a better language. No i am not joking. batch has too many quirks/nuances such as this plus a lot of other limitations, its just not worth the time coming up with ugly/inefficient workarounds. If you are running Windows 7 and higher, why not try using vbscript or even powershell. These tools/language will greatly help in your daily programming/admin tasks. As an example of how vbscript can propely take care of such an issue:
For i=0 To WScript.Arguments.Count-1
WScript.Echo WScript.Arguments(i)
Next
Output:
C:\test>cscript //nologo myscript.vbs a=b c
a=b
c
Note that it properly takes care of the arguments.
#echo off
setlocal enabledelayedexpansion
if %1 neq ~ (
set "n=%*"
for %%a in ("!n: =" "!") do (
set "s=%%a"
if !s:~0^,2!==^"^" (
set "s=!s:" "= !"
set "s=!s:""="!"
)
set "m=!m! !s!"
)
%0 ~ !m!
)
endlocal
shift
echo arg1: %~1
echo arg1: %~2
echo arg1: %~dp3
exit /b
c:> untested.bat a=b c "%USERPROFILE%"
#jeb: here another method
untested.cmd
#echo off
setlocal enabledelayedexpansion
set "param=%*"
set param="%param: =" "%"
for /f "delims=" %%a in ('echo %param: =^&echo.%') do (
set "str=%%a"
if !str:~0^,2!==^"^" (
set "str=!str:^&echo.=!"
set "str=!str:"^"= !"
set str="!str:~1,-1!"
)
set "m=!m! !str!"
)
Call :sub %m%
endlocal & goto :eof
:sub
echo arg1: %~1
echo arg2: %~2
echo arg3: %~3
echo arg4: %~4
goto :eof
Again, I can't explain the unexpected behavior, but there is a very simple solution. Just change your argument references, then quote the argument when invoking as such:
test.bat script:
echo arg1: %~1
echo arg2: %~2
echo arg3: %~3
Invoke:
test.bat "a=b" c
Output:
arg1: a=b
arg2: c
arg3:
Note that referencing the arguments with a tilde (~) removes the leading/trailing quotes from the argument.

Resources