Ok, here's an overly verbose version to play with.
test36.bat
Code: Select all
@setlocal
@echo off
:: echo on & prompt;::$S$T$_
setlocal enabledelayedexpansion
:: path; & dpath; & set pathext=;
set ""=^" & set "{=!CMDCMDLINE!"
echo [!{:*%~dp0=!]
if not "!{:~10,1!"==":" goto chk
if not "!{:~-2,1!"==" " goto chk
(::
!;:"!CMDCMDLINE:*:=*!" ^
:;:"!CMDCMDLINE:~0,1!" ^
::)
:: v v v v v v v v ::
:: ::
:: set "#=^&"
set "#=^&rem;"
:: ::
:: ^ ^ ^ ^ ^ ^ ^ ^ ::
call call:(%%#%%%%%%CMDCMDLINE:**=*%%0%%%%
(::
!;:"!CMDCMDLINE:^^=^!" ^
::)
set "}=!CMDCMDLINE!"
if "!}:~3,1!"==":" set ""=:^"
(::
!;:"!CMDCMDLINE:~0,1!" ^
::)
set "}=!{!"
:: v v v v v v v v ::
:: ::
set "}=!}:/=/0!"
:: set "}=!}:?=/1!"
:: set "}=!}:&=/2!"
set "}=!}:%%=/3!"
:: ::
:: ^ ^ ^ ^ ^ ^ ^ ^ ::
call(:%#%%%CMDCMDLINE:**=!}!%%
(::
!;:"!CMDCMDLINE:^^=^!" ^
:;:"!CMDCMDLINE:/3=%%!" ^
:;:"!CMDCMDLINE:/2=&!" ^
:;:"!CMDCMDLINE:/1=?!" ^
:;:"!CMDCMDLINE:/0=/!" ^
::)
if not !CMDCMDLINE!==!{! echo [CMDCMDLINE FAILURE]
:chk
endlocal & if not "%"%"=="" ( set ""="" ) else set ""=^"
echo [%"%]
echo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if defined _ goto :eof
set _=_
cmd /c ^""%~dp0x&if;exists;2 \..\%~nx0" ^"
cmd /c ^""%~dp0x&%%%%0&;&this \..\%~nx0" ^"
cmd /c ^""%~dp0x^=&%%%%~.bat \..\%~nx0" ^"
cmd /c ^""%~dp0x&%%!&echo.bat \..\%~nx0" "/?" /? ^"
pause
cls
Just give it a trivial file name, like
test36.bat. At the end of the batch it calls itself with crazy characters making it easier to test how the code reacts to various different poison characters and character sequences. To simplify the output, I put in a lazy
echo [!{:*%~dp0=!]; make sure the folders where
test36.bat is located don't contain any poison characters. Remove that line if you insist on running the batch from such a location.
No errors are redirected.
Within the code are two areas marked with
Code: Select all
:: v v v v v v v v ::
:: ^ ^ ^ ^ ^ ^ ^ ^ ::
that denote code that can be commented or uncommented to see how it affects errors. Of note is
set "#=^&rem;" which can be toggled to show how a rem is parsed among the contents of a call. The other marked section contains placeholder characters.
The output of the above is:
Code: Select all
[test36.bat" "]
["]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[x&if;exists;2 \..\test36.bat" "]
["]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[x&%%0&;&this \..\test36.bat" "]
["]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[x^=&%%~.bat \..\test36.bat" "]
The following usage of the path operator in batch-parameter
substitution is invalid: %~.bat \..\test36.bat"%
For valid formats type CALL /? or FOR /?
["]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[x&%!&echo.bat \..\test36.bat" "/?" /? "]
["]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press any key to continue . . .
I've inverted the CMDCMDLINE restoration check to only echo if it fails. Comment out
set "}=!}:%%=/3!" if you want to see it fail.
Edit - See the exclamation point on the line after
(::? While it's well known that a :: on the following line gives an invalid drive letter error, a single : label is actually checking for a file (and failing because a valid file can't start with a colon). With delayed expansion on, starting the line after an intra-parenthesis :: comment with a ! and then at least 1 delimiter (like a semi-colon) SEEMS to be safe. It doesn't trigger error messages, or file access, though it does have some impact on how cmd.exe parses the line as can be seen if you have
echo on.
Edit 2 - Apparently the
!;: lines should be
!;:: otherwise
goto ^"^^^!CMDCMDLINE will be able to jump to them. I didn't know the parser was quite THAT aggressive at searching for labels.
Edit 3 -
!=:: appears to be an even better option than
!;:: since ; can be the name of an env var, while = cannot (correct?). For the use above, ; being defined isn't fatal, but it can be in other situations.
Edit 4 - In my efforts to avoid file i/o I think I actually stumbled upon something useful regarding the
call command. If a
call has the following form:
Code: Select all
call internalcommand/(whatever
:: example:
call set/(x=hello
it appears to avoid hitting the file system. The downside for
set is that your env var has to start with
/( and cannot use the
set syntax where you wrap it all in quotation marks. I think some characters besides
( may work as well, but I haven't fully explored that yet. The other drawback is the internal command has to be flexible enough to cope with
/(. So
set works, as does
echo, but then every line echo'd will start with
(.
It'd be nice to get input from someone else testing to reproduce my results. A simple test scenario is:
where each
echo:: should hit the file system twice (searching for
echo: and
echo::) and with no file system hits between the
echo:: hits.
Queue