Page 1 of 1

Unexpected DOS shell expansion of %~n1 and %~x1

Posted: 28 May 2021 15:51
by sandbelt
I always supposed that the DOS shell has no automatic wildcard expansion. But I have just noticed such an effect with the %~n and %~x syntaxes, which are expanding to the first matching file name and/or extension IF there is such a match, except if the passed parameter is '*.*', which goes totally haywire.

Surely somebody would have documented this before, since I find the same effect in Win7 and above, but I can't find anything online (I would appreciate links if anybody has any).

test-shell-expansion.bat:

Code: Select all

@echo off

call :splitarg *.bat
call :splitarg test*.*
call :splitarg :test*.*
call :splitarg 
call :splitarg *.*
call :splitarg :*.*
exit /b 0


:splitarg
echo arg = %~1, name = %~n1, ext = %~x1
goto :eof
C:\bat\testing>dir /b
@notes.txt
ExampleCallSub.bat
inline-comments.bat
test-args.bat
test-choice.bat
...

Code: Select all

C:\bat\testing>.\test-shell-expansion.bat
arg = *.bat, name = ExampleCallSub, ext = .bat
arg = test*.*, name = test-args, ext = .bat
arg = :test*.*, name = :test*, ext = .*
arg = , name = , ext =
arg = *.*, name = , ext = .
arg = :*.*, name = :*, ext = .*

Re: Unexpected DOS shell expansion of %~n1 and %~x1

Posted: 29 May 2021 06:09
by penpen
Wildcard expansion rules can be found here:
viewtopic.php?f=3&t=6207#p39420.

The "*.*" case doesn't go haywire at all - the first match in your cases is the actual directory (".").


penpen

Re: Unexpected DOS shell expansion of %~n1 and %~x1

Posted: 30 May 2021 15:25
by sandbelt
Thank you, yes, I knew about the expansion rules (interesting in themselves).
And thank you for pointing out that the "*.*" case is regular after all - I had totally overlooked "." and ".." . But that was not the reason for my post.

The effect I am curious about appeared in a simple script to pass wildcard file specifications to DIR. I wanted some enhancements that meant first separating the parameter's name and extension parts. My example code was cut down from this project, though not by enough, and I could have reduced it to a simple one-liner:

splitarg.bat

Code: Select all

@echo arg = %~1, name = %~n1, ext = %~x1

Code: Select all

C:\bat\testing>splitarg *.bat
arg = *.bat, name = ExampleCallSub, ext = .bat
My point was to note that %~n1 is automatically expanded (if possible) by the DOS shell to an actual file name, though %~1 is never expanded. I can now see that this behaviour is inevitable if the %~px and %~fx syntaxes are going to work. But it does mean that designers who want to pass wildcard filenames to a script cannot simply use %~n1 and %~x1 to split them.

My own workaround, for anybody else who may have this use case, is to prefix the file spec with an illegal character and remove it after doing the name splitting:

Code: Select all

@echo off
setlocal
set arg=%~1
call :wildcard_func ":%arg%"
exit /b 0

:wildcard_func
set s=%~n1
set name=%s:~1%
set ext=%~x1
echo arg = %arg%, name = %name%, ext = %ext%
goto :eof

Code: Select all

C:\bat\testing>safesplitarg *.bat
arg = *.bat, name = *, ext = .bat
This at least is simpler than trying to use bat file string manipulations :wink:

Re: Unexpected DOS shell expansion of %~n1 and %~x1

Posted: 30 May 2021 19:12
by penpen
The expansion of wildcards within batch- and for-variable-expansion and is normal and i'm also pretty sure it was discussed here somewhere, but at the moment i can't find it.
However, if i remember right, then the reason why your specific solution works is because the doublecolon character is an Alternate Data Stream specifier; astersik characters ('*') are valid letters for ADS-names (and therefore are not specified as wildcards).

Example:

Code: Select all

@echo off
setlocal enableExtensions disableDelayedExpansion
>"test.txt" echo(test.txt
>"test.txt:*.bat" echo(ADS:*.bat
set "test="
set test
set /p "test=" <"test.txt:*.bat"
set test
goto :eof
Sidenote: Slashes ('/') should also work, but i'm sceptic of any other character.


penpen

Re: Unexpected DOS shell expansion of %~n1 and %~x1

Posted: 31 May 2021 04:14
by jeb
Hi,

for me it was unexpected, that wildcards are expanded, if the content is in %1..%9 or even in a FOR meta variable.

Code: Select all

@echo off
echo %%1=%1         - no wildcard expansion
echo %%~1=%~1     - no wildcard expansion
echo %%~n1=%~n1 - wildcard expansion
echo %%~f1=%~f1    - wildcard expansion