- Is self-contained and requires no temp files or secondary batch scripts
- Works with any input path/filename, regardless of special characters or Unicode.
After trying various things suggested here and elsewhere (SO, etc) it seemed that even if one part of what I tried was successful some other part made it fail (eg: an otherwise working escaped replacement suddenly behaving differently or not working at all when placed in a necessary for loop). Quite frustrating
After racking my head for ideas (not helped by my limited proficiency of batch ) I thought why not instead transport the length of each input past the endlocal barrier instead of the actual string itself, then trim the original argument? It seems that the trim function doesn't interfere with the original characters and so I tried it and to my delight it worked with any input filename I tried!
Now granted I'm not an uber-expert like some here but figured I'd share for thoughts and also in case it's useful for others in a similar situation. There may also be some better method that fulfills those aims that I just didn't find. In the version I made it assumes a cmd.exe path with the /c option and the batch script path in the arguments (ie: the script launched from File Explorer/Send To/Run). I read on SS64.org that these two things are omitted when launched directly from the command line which could be accounted for in with a conditional if/else but I didn't here.
Sample filenames to test with (also tested special characters in batch script path and in batch script name). See this post for additional test paths specifically with ampersands in no space paths.
Code: Select all
Example [! !§$%&()=`´'_;,.-#+´^ßöäüÖÄÜ°^^#].png Example [עִבְרִית].png Example [ファイル名の例].png Example&Another&&.png
Overview of scripts embedded below and in the following post:
- New-method.bat = the method which splits just the batch script path itself and the input arguments into separate variables
- New-method-with-sub-path-example.bat = same example but showing how sub-paths (eg: %%~pi, %%~ni) can be obtained as well.
- Standard-method-1.bat = typical method of obtaining batch script path and input arguments (using %1, etc). Fails with problematic special characters.
- Standard-method-2.bat = simple cmdcmdline method but not accounting for special characters. Setlocal DisableDelayedExpansion could be used but leads to recursion issues with enough inputs if not paired with an endlocal.
Code: Select all
@echo off setlocal enableextensions enabledelayedexpansion rem New line variable (requires the two empty lines beneath) set lf=^ set "count=0" rem Batch script self path for %%f in ("!cmdcmdline!") do ( setlocal disabledelayedexpansion set scr="%~f0" setlocal enabledelayedexpansion call :len scr scrlen for /f "delims=" %%a in ("!scrlen!") do endlocal & endlocal & set "scrlen=%%a" ) rem Trim the cmd.exe path and /c (assumes launched from File Explorer/Send To/Run) set "cmd=!cmdcmdline:~32!" & call set scr=%%cmd:~0,!scrlen!%% rem Trim script path and trailing quote from arguments variable for %%b in (!scrlen!) do set "args=!cmd:~%%b,-1!" & set "args=!args:* =!" rem Account for input paths both with and without spaces set args="!args:"=!" for /f "tokens=1 delims=:" %%d in (!args!) do ( rem Split into new lines based on drive letter for %%l in ("!lf!") do ( set args=!args:%%d:\=%%l%%d:\! ) ) set "args=!args: "="!" rem Remove empty first new line and leading double quote set "args=!args:~2!" for %%f in (!args!) do ( set /a "count+=1" setlocal disabledelayedexpansion set infull="%%~f" setlocal enabledelayedexpansion rem Prepare length call :len infull infulllen rem Send past endlocal barrier for /f "delims=" %%a in ("!infulllen!") do ( endlocal & endlocal for %%i in (!count!) do set "infulllen[%%i]=%%a" ) for %%i in (!count!) do ( rem Obtain each input's full path by trimming the full arguments variable using its length value for %%l in (!infulllen[%%i]!) do set "infull[%%i]=!args:~0,%%l!" rem Trim space left over from previous argument set "args=!args:~1!" rem Trim the same length from !args! after each input, for the next for %%l in (!infulllen[%%i]!) do set "args=!args:~%%l!" rem Remove left-over new line character set "infull[%%i]=!infull[%%i]:~1!" set "infull[%%i]=!infull[%%i]:"=!" set infull[%%i]="!infull[%%i]!" ) ) rem Test output echo Script path: !scr! & echo. for /l %%i in (1,1,!count!) do ( echo Input %%i -------------------------------- & echo. echo !infull[%%i]! & echo. & echo. ) pause exit /b :len set "s=!%~1!#" set "len=0" for %%p in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do ( if "!s:~%%p,1!" neq "" ( set /a "len+=%%p" set "s=!s:~%%p!" ) ) endlocal set "%~2=!len!" exit /b endlocal