I try to explain this a bit with an example.FOR /? wrote:%~$PATH:I Searches the directories listed in the PATH environment variable and expands %I to the fully qualified name of the first directory found. If the environment variable name isn't defined or the file isn't found by the search, this modifier expands to the empty string.
Code: Select all
@echo off
mkdir subdir
echo X > subdir\dummy.tmp
setlocal EnableDelayedExpansion
set "__definedVar=%~dp0subdir"
set "undefinedVar="
set "p=%%"
FOR %%X in (dummy.tmp) do (
FOR %%Y in (%~dp0subdir\dummy.tmp) do (
echo X contains %%X
echo Y contains %%Y
echo ------ Using X -------
echo #1 !p!!p!~$__definedVar:X = %%~$__definedVar:X
echo #2 !p!!p!~$undefinedVar:X = %%~$undefinedVar:X
echo #3 !p!!p!~$:X = %%~$:X
echo ------ Using Y -------
echo #4 !p!!p!~$__definedVar:Y = %%~$__definedVar:Y
echo #5 !p!!p!~$undefinedVar:Y = %%~$undefinedVar:Y
echo #6 !p!!p!~$:Y = %%~$:Y
echo ------ Using Z -------
echo #7 !p!!p!~$__definedVar:Z = %%~$__definedVar:Z
echo #8 !p!!p!~$undefinedVar:Z = %%~$undefinedVar:Z
echo #9 !p!!p!~$:Z = %%~$:Z
)
)
Code: Select all
X contains dummy.tmp
Y contains E:\syntax\subdir\dummy.tmp
------ Using X -------
#1 %%~$__definedVar:X = E:\syntax\subdir\dummy.tmp
#2 %%~$undefinedVar:X =
#3 %%~$:X =
------ Using Y -------
#4 %%~$__definedVar:Y = E:\syntax\subdir\dummy.tmp
#5 %%~$undefinedVar:Y =
#6 %%~$:Y = E:\syntax\subdir\dummy.tmp
------ Using Z -------
#7 %%~$__definedVar:Z = %~$__definedVar:Z
#8 %%~$undefinedVar:Z = %~$undefinedVar:Z
#9 %%~$:Z = %~$:Z
1) Undefined variables and an empty variable names behave differently (#5 and #6). Perhaps an empty variable name accesses some random existing variable
2) If the meta variable isn't defined, the complete expression does not expand (#7, #8, #9)
3) If the meta variable contains an absolute filename, the searchVariable is no longer used, only the meta variable is used.
instead the absolute filename is used
4) If the meta variable contains a relative filename (starts with a dot or backslash), the searchVariable won't be used anymore, instead the relative filename will be appended to the current directory use
And here are a few other findings:
5) The tilde-dollar expression work also outside of FOR-Loops with normal percent expansion, but only the meta-vars 0..9 are valid
Code: Select all
echo %~$PATH:1 -- works always
echo %~$PATH:9 -- works always
echo %~$PATH:A -- fails with a syntax error
echo %~$PATH:* -- fails with a syntax error
6) The expansion in FOR-Loops happens just BEFORE a loop block is executed, but the expansion is done for each loop!
Code: Select all
setlocal EnableDelayedExpansion
set "var=invalid-path"
FOR %%# in (calc.exe CALC.EXE) DO (
set "var=C:\windows\system32"
echo var='!var!' expr = %%~$var:#
)
Code: Select all
var='C:\windows\system32' expr =
var='C:\windows\system32' expr = C:\Windows\System32\calc.exe
Code: Select all
setlocal
set "var=is defined"
set "undef="
set "DATE=never set a pseudo variable"
FOR %%# in (C:\windows) DO (
echo #1 var = %%~$var:#
echo #2 undef = %%~$undef:#
echo #3 time = %%~$time:#
echo #4 cd = %%~$cd:#
echo #5 __cd__ = %%~$__cd__:#
echo #6 cmdcmdline = %%~$cmdcmdline:#
echo #7 date = %%~$date:#
)
Code: Select all
#1 var = C:\Windows
#2 undef =
#3 time =
#4 cd =
#5 __cd__ =
#6 cmdcmdline =
#7 date = C:\Windows
- You can't create a searchVariable where the name is invalid. In other words every searchVariable can be crafted
Code: Select all
FOR %%# in (C:\windows) DO (
echo #1 %%~$varcraft:#
echo #2 %%~$=varcraft=:#
echo #3 %%~$, =;varcraft, =; Even this can be defined:#
- Remove all delimiters (<space>, <tab>, comma, semi colon, equal sign) until the first non delimiter is found
- Only the last space stops the name (this space isn't part of the name)
- The parsing ends at the first colon, obviously a colon can not be part of any searchVariable name
For expample #3 "%%~$, =;varcraft, =; Even this can be defined:#" a matching variable can be defined by
Code: Select all
set "varcraft, =; Even this can be=content"
Currently I use it in my batch library to detect if meta-variables are defined somewhere in the call chain or not by a simple:
Code: Select all
set "undef="
FOR %%1 in (1) do (
if "%%~$undef:X" == "" (
echo meta-var X is defined
) else (
echo meta-var X is NOT defined
)
)
Theses characters are weak meta-variables "adfnpstxzADFNPSTXZ", because they are also valid modifiers for meta-variables.
It's only a problem, when using this meta-variables with a tilde, without they are also safe.
This example shows the problem
Code: Select all
@echo off
call :func
echo(
FOR %%s in (hello) DO (
call :func
)
exit /b
:func
for /L %%d in (1 1 2) do (
echo Count: %%d Duration %%~dseconds
)
This can be solved by adding a tilde-dollar expressionCount: 1 Duration 1seconds
Count: 2 Duration 2seconds
Count: 1 Duration E:econds
Count: 2 Duration E:econds
Code: Select all
@echo off
setlocal DisableDelayedExpansion
REM *** The $FOR-BREAK can be used for the expansion of WEAK for-variables with tilde
REM *** %% ~f % $FOR-BREAK% (without spaces)
REM *** This works as long as no percent meta-variable exists
set "$FOR-BREAK=%%~$=_FOR-variable_DOLLAR_is_required_=:$"
call :func
echo(
FOR %%s in (hello) DO (
call :func
)
exit /b
:func
for %%$ in (1) do for /L %%d in (1 1 2) do (
echo Count: %%d Duration %%~d%$FOR-BREAK%seconds
)
----Count: 1 Duration 1seconds
Count: 2 Duration 2seconds
Count: 1 Duration 1seconds
Count: 2 Duration 2seconds
These were some notes on tilde-dollar expressions.
I hope that some interesting aspects were included.
jeb