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

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
sandbelt
Posts: 2
Joined: 28 May 2021 15:26

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

#1 Post by sandbelt » 28 May 2021 15:51

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 = .*

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

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

#2 Post by penpen » 29 May 2021 06:09

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

sandbelt
Posts: 2
Joined: 28 May 2021 15:26

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

#3 Post by sandbelt » 30 May 2021 15:25

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:

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

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

#4 Post by penpen » 30 May 2021 19:12

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

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

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

#5 Post by jeb » 31 May 2021 04:14

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

Post Reply