Batch file to replace special characters in subfolder in windows - batch-file

#echo off
FOR /f "delims=" %%G IN ('dir /a-d /b /s /o-n ^|sort /r') DO (
setlocal enabledelayedexpansion
pushd "%%~dpG"
SET Var=%%~nfG
SET Var=!Var: =_!
SET Var=!Var:[=_!
SET Var=!Var:]=_!
SET Var=!Var:(=_!
SET Var=!Var:)=_!
SET Var=!Var:,=_!
SET Var=!Var:'=_!
rename "%%~nfG" "!Var!"
popd
endlocal
)
Not working getting error in prompt like
_! was unexpected at this time.
Can anyone answer or correct this plz.enter code here

Also I'd compare if the name is changed before trying to rename:
:: Q:\Test\2017\08\05\SO_45521689.cmd
#echo off
FOR /f "delims=" %%G IN (
'dir /a-d /b /s /o-n ^|findstr "[,'()\[\]]"^|sort /r'
) DO (
setlocal enabledelayedexpansion
pushd "%%~dpG"
SET "Var=%%~nfG"
SET "Var=!Var: =_!"
SET "Var=!Var:[=_!"
SET "Var=!Var:]=_!"
SET "Var=!Var:(=_!"
SET "Var=!Var:)=_!"
SET "Var=!Var:,=_!"
SET "Var=!Var:'=_!"
if "%%~nfG" neq "!Var!" rename "%%~nfG" "!Var!"
popd
endlocal
)

SET Var=!Var:)=_!
rem ↑ this closing parenthesis closes the `... DO ()` code block.
Use
SET "Var=!Var:)=_!"
or
SET Var=!Var:^)=_!
Moreover, you cannot specify a drive or path for rename target. Use SET "Var=%%~nxG" instead of SET Var=%%~nfG.
Be aware of that your script ingurgitates all exclamation marks if a file name contains any.
The following script should do the job keeping delayed expansion disabled. Please note that operational rename command is merely ECHOed for debugging purposes.
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
rem choose initial directory
pushd "D:\bat\Unusual Names"
FOR /f "delims=" %%G IN ('dir /a-d /b /s /o-n') DO (
SET "_FFN=%%~fG" File Full Name
SET "Var=%%~nxG" File Name + Extension
call :renaming
)
popd
ENDLOCAL
goto :eof
:renaming
SET "Var=%Var: =_%"
SET "Var=%Var:[=_%"
SET "Var=%Var:]=_%"
SET "Var=%Var:(=_%"
SET "Var=%Var:)=_%"
SET "Var=%Var:,=_%"
SET "Var=%Var:'=_%"
ECHO rename "%_FFN%" "%Var%"
goto :eof

Related

Why does batch treat an array entry different from singular?

I have the following (simplified etc):
#echo off
set searchStr="AAAA"
set workingPaths[0]="C:\Docs\Me\"
set x=0
::Loop1
if defined workingPaths[%x%] (
set currPath=%%workingPaths[%x%]%%
rem set currPath="C:\Docs\Me\"
call echo Searching in: %currPath%
for %%f in (%currPath%*.doc*) do (
findstr /s /m /I /c:%searchStr% "%%f"
)
set /a x+=1
GOTO :Loop1
)
It works perfectly fine if I were to switch the currPath to a singular assignment (where rem is currently), but won't work if the currPath is assigned out of the array.
The subsequent echo is identical regardless of whether currPath is set from the array or the singular assignment.
Anyone any ideas where I'm going wrong? Thanks in advance
I would perform this task a little differently, in that I'd try to better utilise the built-in options of findstr.exe. I would build a listing of the workingPaths and pass those to a single findstr.exe instance, using its /D option.
Example:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Script defined variables [please do not modify].
For /F "Delims==" %%G In ('"(Set workingPaths[) 2>NUL"') Do Set "%%G="
Set "findStr=%SystemRoot%\System32\findstr.exe"
Set "dirList="
Rem User defined variables [modify as necessary]
Set "searchStr=AAAA"
Set "searchGlob=*.doc"
Set "workingPaths[0]=C:\Docs\Me"
Set "workingPaths[1]=C:\Users\Me\Documents"
Set "workingPaths[2]=C:\Users\Me\Desktop"
Rem Directory list builder [please do not modify]
For /F "Tokens=1,* Delims==" %%G In ('"(Set workingPaths[) 2>NUL"'
) Do If Not Defined dirList (Set "dirList="%%~H"") Else (
SetLocal EnableDelayedExpansion
For /F "UseBackQ Delims=" %%I In ('"!dirList!"') Do (EndLocal
Set "dirList=%%~I;"%%~H""))
If Not Defined dirList Exit /B
Rem Main search command [please do not modify]
%findStr% /D:%dirList% /I /L /M /S "%searchStr%" "%searchGlob%"
Rem Optional commands for GUI usage puposes only [remove as necessary]
Pause
GoTo :EOF
I changed
set currPath=%%workingPaths[%x%]%%
to
set currPath=!workingPaths[%x%]!
and its working... albeit it doesn't seem to like iterating the last array entry ('cos I was stupid and typo'd it!).
Leaving a final working solution of:
#echo off
set searchStr="AAAA"
set workingPaths[0]="C:\Docs\Me\"
setlocal enabledelayedexpansion
set x=0
::Loop1
if defined workingPaths[%x%] (
set currPath=!workingPaths[%x%]!
rem set currPath="C:\Docs\Me\"
call echo Searching in: %currPath%
for %%f in (%currPath%*.doc*) do (
findstr /s /m /I /c:%searchStr% "%%f"
)
set /a x+=1
GOTO :Loop1
)

Replacing text in a file name

What I am trying to do is replace part of a file name with my computer name.
#echo off
set host=%COMPUTERNAME%
set host=%host:~4, -2%
for /f "delims=" %%a in ('dir /a:-d /o:n /b') do call :next "%%a"
pause
GOTO:EOF
:next
set "newname=%~nx1"
set "newname=%newname:XXXX=zzzz%"
echo ren %1 "%newname%
When I run the above, it replaces the XXXX's with zzzz's
When I change set "newname=%newname:XXXX=zzzz%" to set "newname=%newname:XXXX=%host%"it just deletes the X's.
What happens if you use delayed expansion?
#Echo Off
SetLocal EnableDelayedExpansion
Set "Host=%COMPUTERNAME:~4,2%"
For /F "Delims=" %%A In ('Dir /B/A-D/ON') Do (Set "NewName=%%~nA"
Echo Ren "%%~A" "!NewName:XXXX=%Host%!%%~xA"
Pause
GoTo :EOF

rename text from file ,and string batch file

i have a lot of folders , with in then there are files with name : XXXX_transcoded.j2c
i need to remove the _transcoded.j2c from the file and update to XXXX.txt
anyone have any idead how to do it
here my code so far
for /r %%i in (*.j2c) do (
call:Set %%~ni
)
:Set
set currenttext=%*
set currenttext=%currenttext:_transcoded=%
echo %currenttext%
%%~ni.Contract.xml %%i.txt
pause
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /s /b /a-d "%sourcedir%\*_transcoded.j2c" '
) DO (
SET "fullname=%%a"
SET "oldname=%%~nxa"
CALL :changename
)
GOTO :EOF
:changename
SET "newname=%oldname:_transcoded.j2c=%"
ECHO REN "%fullname%" "%newname%.txt"
GOTO :eof
This should work for you. You'd have to set your directory into sourcedir.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO REN to REN to actually rename the files.
#echo off
setlocal disableDelayedExpansion
for /f "eol=: delims=" %%F in (
'dir /s /b /a-d *_transcoded.j2c'
) do (
set "full=%%F"
set "name=%%~nxF"
setlocal enableDelayedExpansion
ren "!full!" "!name:~0,-15!.txt"
endlocal
)
This should be all you need. Test it on some sample files.
#echo off
for /r %%i in (*_transcoded.j2c) do (
for /f "delims=_" %%a in ("%%~nxi") do ren "%%i" "%%a.txt"
)

Rename Batch File Has To Be In Same Folder

For some reason my code below only works if the batch file is in the same folder as the files to be renamed even though i've specified the path. When the batch file is in a different folder I receive an error saying the file can't be found. Any input on this?
#echo off&setlocal
set "name1=Bart"
set "name2=Carl"
set "name3=Judy"
for /f "delims=" %%a in ('dir C:\Users\%username%\Downloads\Export_*.csv /b /a-d /o-d') do (
set "fname=%%~a"
set /a counter+=1
SETLOCAL ENABLEDELAYEDEXPANSION
call set "nname=%%name!counter!%%"
ren "!fname!" "!nname!%%~xa"
endlocal
)
just add a working path:
#echo off&setlocal
set "workingpath=%userprofile%\Downloads"
set "name1=Bart"
set "name2=Carl"
set "name3=Judy"
for /f "delims=" %%a in ('dir "%workingpath%\Export_*.csv" /b /a-d /o-d') do (
set "fname=%%~a"
set /a counter+=1
SETLOCAL ENABLEDELAYEDEXPANSION
call set "nname=%%name!counter!%%"
ren "%workingpath%\!fname!" "!nname!%%~xa"
endlocal
)
Endoro has a good working solution for the stated problem. Another option is to simply PUSHD to where the files are located. Then you no longer need to include the path in the remainder of the code.
Other points unrelated to the question:
It is probably a good idea to initialize counter to 0, just in case some other process already set the value to a number.
You don't really need the nname variable.
I prefer to transfer the counter value to a FOR variable so that I don't need to use the CALL construct. (For those that don't know, the delayed expansion toggling is to protect ! characters that may be in the file name).
#echo off
setlocal
set "name1=Bart"
set "name2=Carl"
set "name3=Judy"
pushd "C:\Users\%username%\Downloads"
set /a counter=0
for /f "delims=" %%a in ('dir Export_*.csv /b /a-d /o-d') do (
set "fname=%%~a"
set /a counter+=1
setlocal enableDelayedExpansion
for %%N in (!counter!) do (
endlocal
ren "!fname!" "!name%%N!.csv"
)
)
popd
Finally, FINDSTR with the /N option can eliminate the need for CALL or additional FOR
#echo off
setlocal
set "name1=Bart"
set "name2=Carl"
set "name3=Judy"
pushd "C:\Users\%username%\Downloads"
for /f "tokens=1* delims=:" %%A in (
'dir Export_*.csv /b /a-d /o-d ^| findstr /n "^"'
) do (
set "fname=%%~B"
setlocal enableDelayedExpansion
ren "!fname!" "!name%%A!.csv"
endlocal
)
popd
#cbmanica is right: the directory is not being included in the variable fname, so you'll have to specify it manually in the ren command.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set "name1=Bart"
set "name2=Carl"
set "name3=Judy"
set "dir=C:\Users\%username%\Downloads\"
for /f "delims=" %%a in ('dir %dir%Export_*.csv /b /a-d /o-d') do (
set "fname=%%~a"
set /a counter+=1
:: <Comment> In the below line is the use of "call" necessary? </Comment>
call set "nname=%%name!counter!%%"
ren "!dir!!fname!" "!dir!!nname!%%~xa"
)
endlocal
That should do exactly what you want.

Get full path and long file name from short file name

I have an nice console file manager (eXtreme by Senh Liu), it passes short path/filenames as variables to a menu.bat.
How can I generate a full folder name + long file name?
Example:
input variable = "P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL"
target variable = "P:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal"
I have tried the following:
SET my_file=%~2:
echo %my_file% produces: "P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL"
FOR /F "tokens=* USEBACKQ" %%F IN (`dir /B %2`) DO SET my_file=%%~fF:
echo %my_file% produces: "P:\MYPROG~1\SHELLS\zBackup\RefsToMyData.bal"
FOR /F "tokens=* USEBACKQ" %%F IN (`dir /B %2`) DO SET my_file=%%~dpnxF:
echo %my_file% produces: "P:\MYPROG~1\SHELLS\zBackup\RefsToMyData.bal"
Simple solution: use PowerShell.
PS C:\> (Get-Item 'P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL').FullName
P:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal
You can incorporate a PowerShell call in a batch file like this:
#echo off
setlocal
for /f "usebackq delims=" %%f in (
`powershell.exe -Command "(Get-Item '%~1').FullName"`
) do #set "var=%%~f"
echo %var%
Output:
C:\> test.cmd P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL
P:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal
PowerShell is available for all supported Windows versions:
Windows XP SP3 and Server 2003 SP2: PowerShell v2 available
Windows Vista and Server 2008: ship with PowerShell v1 (not installed by default), PowerShell v2 available
Windows 7 and Server 2008 R2: PowerShell v2 preinstalled, PowerShell v3 available (batteries not included)
Windows 8 and Server 2012: PowerShell v3 preinstalled
If PowerShell can't be used for some reason (e.g. administrative restrictions), I'd use VBScript instead:
name = WScript.Arguments(0)
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(name) Then
Set f = fso.GetFile(name)
ElseIf fso.FolderExists(name) Then
Set f = fso.GetFolder(name)
If f.IsRootFolder Then
WScript.Echo f.Path
WScript.Quit 0
End If
Else
'path doesn't exist
WScript.Quit 1
End If
Set app = CreateObject("Shell.Application")
WScript.Echo app.NameSpace(f.ParentFolder.Path).ParseName(f.Name).Path
A VBScript like the one above can be used in a batch file like this:
#echo off & setlocal
for /f "delims=" %%f in ('cscript //NoLogo script.vbs "%~1"') do #set "var=%%~f"
echo %var%
This does require an additional script file, though.
The following should work with any valid path, as long as it is not a UNC path. The path may be absolute or relative. It may use short file names or long names (or a mixture). The path may refer to a folder or a file.
The result will end with \ if it is a folder, no \ at end if it is a file.
The :getLongPath routine expects an inputPath variable name as the 1st argument, and an optional return variable name as the 2nd argument. The inputPath variable should contain a valid path. If the return variable is not speciied, then the result is ECHOed to the screen (enclosed in quotes). If the return variable is specified, then the result is returned in the variable (without quotes).
The routine should only be called when delayed expansion is disabled if you are returning a variable. If called with delayed expansion enabled, then the result will be corrupted if it contains the ! character.
Test cases (for my machine only) are at the top of the script, the actual routine at the bottom.
#echo off
setlocal
for %%F in (
"D:\test\AB2761~1\AZCFE4~1.TXT"
"AB2761~1\AZCFE4~1.TXT"
"D:\test\AB2761~1\ZZCE57~1\"
"D:\test\a b\a z.txt"
"D:\test\a b\z z"
"."
"\"
"x%%&BAN~1\test"
"x%% & bang!\test"
) do (
echo(
echo resolving %%F
set "shortPath=%%~F"
call :getLongPath shortPath longPath
set longPath
)
echo(
echo(
set "shortPath=D:\test\AB2761~1\AZCFE4~1.TXT"
set shortPath
echo Calling :getLongPath with with no return variable
call :getLongPath shortPath
exit /b
:getLongPath path [rtnVar]
setlocal disableDelayedExpansion
setlocal enableDelayedExpansion
for %%F in ("!%~1!") do (
endlocal
set "sourcePath=%%~sF"
set "sourceFile=%%~nxF"
)
if not exist "%sourcePath%" (
>&2 echo ERROR: Invalid path
exit /b 1
)
set "rtn="
2>nul cd "%sourcePath%" || (
cd "%sourcePath%\.."
for /f "eol=: delims=" %%F in ('dir /b /a-d "%sourceFile%"') do set "rtn=%%F"
)
:resolveFolders
for %%F in ("%cd%") do (
cd ..
set "folder=%%~nxF"
)
if defined folder for /f "eol=: delims=" %%: in ('dir /b /ad') do (
if /i "%%~snx:" equ "%folder%" (
set "rtn=%%:\%rtn%"
goto :resolveFolders
)
)
set "rtn=%cd%%rtn%
( endlocal
if "%~2" equ "" (echo "%rtn%") else set "%~2=%rtn%"
)
=== OUTPUT ===
resolving "D:\test\AB2761~1\AZCFE4~1.TXT"
longPath=D:\test\a b\a z.txt
resolving "AB2761~1\AZCFE4~1.TXT"
longPath=D:\test\a b\a z.txt
resolving "D:\test\AB2761~1\ZZCE57~1\"
longPath=D:\test\a b\z z\
resolving "D:\test\a b\a z.txt"
longPath=D:\test\a b\a z.txt
resolving "D:\test\a b\z z"
longPath=D:\test\a b\z z\
resolving "."
longPath=D:\test\
resolving "\"
longPath=D:\
resolving "x%&BAN~1\test"
longPath=D:\test\x% & bang!\test\
resolving "x% & bang!\test"
longPath=D:\test\x% & bang!\test\
shortPath=D:\test\AB2761~1\AZCFE4~1.TXT
Calling :getLongPath with with no return variable
"D:\test\a b\a z.txt"
If you want to run the above code, then I suggest you completely delete all the test scenario code between #echo off and :getLongPath. Then you can simply call the script, passing any valid path as the first argument. The correct long path should be printed as a result.
I was amazed how difficult this was to solve using batch. I don't think it is much easier with JScript or VBS (Actually, Ansgar found a nice VBS solution). But I like Ansgar's simple PowerShell solution - so much easier.
Update
I found an obscure case where the above code fails if called from within a FOR loop, and the path happens to have the FOR variable within it. It also doesn't properly report a path with wild cards as an error, and it doesn't work with delayed expansion enabled when the path contains !.
So I created a modified version below. I'm pretty confident it should truly work in all situations, except for UNC paths and possibly not with unicode in the path. I packaged it up as an easy to call procedure, complete with built in documentation. It can be left as a stand-alone script, or incorporated into a larger script.
#echo off
:getLongPath
:::
:::getLongPath PathVar [RtnVar]
:::getLongPath /?
:::
::: Resolves the path contained in PathVar into the full long path.
::: If the path represents a folder then it will end with \
:::
::: The result is returned in variable RtnVar.
::: The result is echoed to the screen if RtnVar is not specified.
:::
::: Prints this documentation if the first argument is /?
if "%~1" equ "" (
>&2 echo ERROR: Insufficient arguments. Use getLongPath /? to get help.
exit /b 1
)
if "%~1" equ "/?" (
for /f "delims=" %%A in ('findstr "^:::" "%~f0"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
echo(!ln:~3!
endlocal
)
exit /b 0
)
setlocal
set "notDelayed=!"
setlocal disableDelayedExpansion
setlocal enableDelayedExpansion
for /f "eol=: delims=" %%F in ("!%~1!") do (
endlocal
set "sourcePath=%%~sF"
set "sourcePath2=%%F"
set "sourceFile=%%~nxF"
)
if not exist "%sourcePath%" (
>&2 echo ERROR: Invalid path
exit /b 1
)
set "sourcePath3=%sourcePath2:**=%"
set "sourcePath3=%sourcePath3:?=%"
if "%sourcePath3%" neq "%sourcePath2%" (
>&2 echo ERROR: Invalid path
exit /b 1
)
set "rtn="
2>nul cd "%sourcePath%" || (
cd "%sourcePath%\.."
for /f "eol=: delims=" %%F in ('dir /b /a-d "%sourceFile%"') do set "rtn=%%F"
)
:resolveFolders
for %%F in ("%cd%") do (
cd ..
set "folder=%%~nxF"
)
if defined folder for /f "delims=: tokens=1,2" %%A in ("%folder%:%rtn%") do for /f "eol=: delims=" %%F in ('dir /b /ad') do (
if /i "%%~snxF" equ "%%A" (
set "rtn=%%F\%%B"
goto :resolveFolders
)
)
set "rtn=%cd%%rtn%"
if not defined notDelayed set "rtn=%rtn:^=^^%"
if not defined notDelayed set "rtn=%rtn:!=^!%"
if not defined notDelayed (set "!=!==!") else set "!="
for %%A in ("%rtn%") do (
endlocal
endlocal
if "%~2" equ "" (echo %%~A%!%) else set "%~2=%%~A"!
)
I also adapted Ansgar's VBS into a hybrid JScript/batch script. It should provide the identical result as the pure batch script above, but the JScript is much simpler to follow.
#if (#X)==(#Y) #end /* harmless hybrid line that begins a JScrpt comment
#echo off
:getLongpath
:::
:::getLongPath PathVar [RtnVar]
:::getLongPath /?
:::
::: Resolves the path contained in PathVar into the full long path.
::: If the path represents a folder then it will end with \
:::
::: The result is returned in variable RtnVar.
::: The result is echoed to the screen if RtnVar is not specified.
:::
::: Prints this documentation if the first argument is /?
::************ Batch portion ***********
if "%~1" equ "" (
>&2 echo ERROR: Insufficient arguments. Use getLongPath /? to get help.
exit /b 1
)
if "%~1" equ "/?" (
for /f "delims=" %%A in ('findstr "^:::" "%~f0"') do (
set "ln=%%A"
setlocal enableDelayedExpansion
echo(!ln:~3!
endlocal
)
exit /b 0
)
setlocal
set "notDelayed=!"
setlocal disableDelayedExpansion
set "rtn="
for /f "delims=" %%A in ('cscript //E:JScript //nologo "%~f0" %*') do set "rtn=%%A"
if not defined rtn exit /b 1
if not defined notDelayed set "rtn=%rtn:^=^^%"
if not defined notDelayed set "rtn=%rtn:!=^!%"
if not defined notDelayed (set "!=!==!") else set "!="
for %%A in ("%rtn%") do (
endlocal
endlocal
if "%~2" equ "" (echo %%~A%!%) else set "%~2=%%~A"!
)
exit /b 0
************ JScript portion ***********/
var env=WScript.CreateObject("WScript.Shell").Environment("Process");
var fso=WScript.CreateObject("Scripting.FileSystemObject");
var app=WScript.CreateObject("Shell.Application");
var inPath=env(WScript.Arguments.Item(0));
var folder="";
var f;
if (fso.FileExists(inPath)) {
f=fso.GetFile(inPath);
}
else if (fso.FolderExists(inPath)) {
folder="\\"
f=fso.GetFolder(inPath);
if (f.IsRootFolder) {
WScript.StdOut.WriteLine(f.Path);
WScript.Quit(0);
}
}
else {
WScript.StdErr.WriteLine('ERROR: Invalid path');
WScript.Quit(1);
}
WScript.StdOut.WriteLine( app.NameSpace(f.ParentFolder.Path).ParseName(f.Name).Path + folder);
This returns the full long pathname, but depends on:
A) there not being too many files in the tree (due to time taken)
B) there is only one of the target (long) filename in the tree.
#echo off
for /f "delims=" %%a in (' dir /b "%~1" ') do set "file=%%a"
for /f "delims=~" %%a in ("%~dp1") do cd /d "%%a*"
for /f "delims=" %%a in ('dir /b /s /a-d "%file%" ') do set "var=%%a"
echo "%var%"
When called with mybat "d:\MYPROG~1\SHELLS\zBackup\REFSTO~1.BAL"
it returned this:
"d:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal"
And one unexpectedly simple solution:
echo lcd %some_path%|ftp
EDITED to show example: it isn't 100%
d:\>echo lcd C:\Files\Download\MYMUSI~1\iTunes\ALBUMA~1 |ftp
Local directory now C:\Files\Download\MYMUSI~1\iTunes\Album Artwork.
this is an ugly batch job and my code is not nice, but brut force :-)
#echo off &SETLOCAL
SET "short=P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL"
SET "shorty=%short:\= %"
FOR %%a IN (%short%) DO SET "shortname=%%~nxa"
FOR %%a IN (%shorty%) DO (
IF DEFINED flag (
CALL :doit "%%~a"
) ELSE (
SET "longpath=%%~a"
SET flag=true
SET "first=\"
)
)
ECHO "%longpath%"
goto:eof
:doit
SET "last=%~1"
IF "%last%" neq "%shortname%" (SET "isDir=/ad") ELSE SET "isDir=/a-d"
FOR /f "delims=" %%b IN ('dir %isdir% %longpath%%first%^|findstr /ri "\<%last%\>"') DO SET "X0=%%b"
FOR /f "delims=" %%b IN ('dir %isdir% /x %longpath%%first%^|findstr /ri "\<%last%\>"') DO SET "X1=%%b"
REM for European time format
IF "%X0: =%"=="%X1: =%" (SET /a token=3) ELSE SET /a token=4
REM for "AM/PM" time format
IF "%X0: =%"=="%X1: =%" (SET /a token=4) ELSE SET /a token=5
FOR /f "tokens=%token%*" %%b IN ('dir %isdir% /x %longpath%%first%^|findstr /ri "\<%last%\>"') DO SET "longname=%%~c"
SET "longpath=%longpath%\%longname%"
SET "first="
goto:eof
Please set your time format in the doit function (delete as applicable format).This might maybe fail with special characters in path or file names like !%=&^.
#echo off
setlocal
rem this need to be a short name to avoid collisions with dir command bellow
cd C:\BALBAL~1\BLBALB~1\
set "curr_dir=%cd%"
set "full_path="
:repeat
for /f "delims=" %%f in ('for %%d in ^(.^) do #dir /a:d /n /b "..\*%%~snd"') do (
set "full_path=%%f\%full_path%"
)
cd ..
if ":\" NEQ "%cd:~-2%" (
goto :repeat
) else (
set "full_path=%cd%%full_path%"
)
echo --%full_path%--
cd %curr_dir%
endlocal
The path is hardcoded at the beginning but you can change it or parameterizied it.As you can easy get the full name of a file here is only a solution for directories.
EDIT
now works for file and directory and a parameter can be passed:
#echo off
rem ---------------------------------------------
rem ---------------------- TESTS ----------------
rem ----------------------------------------------
md "c:\test\blablablablabl\bla bla bla\no no no no no no\yes yes yes" >nul 2>&1
md "c:\test\1 b1\1\" >nul 2>&1
for %%t in ("c:\test\blablablablabl\bla bla bla\no no no no no no\yes yes yes") do set t_dir=%%~st
for %%t in ("c:\test\1 b1\1\") do set t_dir2=%%~st
echo a>"%t_dir2%a"
echo a>"%t_dir2%a a.txt"
echo testing "%t_dir%\\"
call :get_full_name "%t_dir%\\"
echo(
echo testing "%t_dir2%a"
call :get_full_name "%t_dir2%a"
echo(
echo testing "%t_dir2%a a.txt" with return variable
call :get_full_name "%t_dir2%a a.txt" test_var
echo return variable : -- %test_var% --
goto :eof
rem -------------------------------------
:get_full_name [%1 - short path to a file or directory ; %2 - if set stores the result in variable with that name]
setlocal
if not exist "%~1" ( echo file/dir does not exist & exit /b 2 )
set "curr_dir=%cd%"
for /f "delims=" %%n in ('dir /b /n "%~dps1\%~snx1"') do set "name=%%n"
cd "%~dps1"
set "full_path="
:repeat
for /f "delims=" %%f in ('for %%d in ^(.^) do #dir /a:d /n /b "..\*%%~snd"') do (
set "full_path=%%~f\%full_path%"
)
cd ..
if ":\" NEQ "%cd:~-2%" (
goto :repeat
) else (
set "full_path=%cd%%full_path%"
)
echo %full_path%%name%
cd %curr_dir%
endlocal & if "%~2" NEQ "" set "%~2=%full_path%%name%"
and the test output:
testing "c:\test\BLABLA~1\BLABLA~1\NONONO~1\YESYES~1\\"
c:\test\blablablablabl\bla bla bla\no no no no no no\yes yes yes\
testing "c:\test\1B1~1\1\a"
c:\test\1 b1\1\a
testing "c:\test\1B1~1\1\a a.txt" with return variable
c:\test\1 b1\1\a a.txt
return variable : -- c:\test\1 b1\1\a a.txt --
And one attempt with WMIC and Win32_Directory.Probably is slower than using cd and dir , but the current directory is not changed:
#echo off
:get_full_name [%1 - short path to a file or directory ; %2 - if set stores the result in variable with that name]
setlocal
if not exist "%~1" ( echo file/dir does not exist & exit /b 2 )
for /f "delims=" %%n in ('dir /b /n "%~dps1\*%~snx1"') do set "name=%%n"
set "short_path=%~dps1"
set "short_path=%short_path:~0,-1%"
set "drive=%short_path:~0,2%"
set "full_name="
:repeat
set "short_path=%short_path:\=\\%"
set "short_path=%short_path:'=\'%"
FOR /F "usebackq skip=2 delims=" %%P in (`WMIC path win32_directory where name^='%short_path%' get Path^,FileName /Format:Textvaluelist.xsl`) do for /f "delims=" %%C in ("%%P") do (
set "_%%C"
)
set "_Path=%_Path:~0,-1%"
set full_name=%_FileName%\%full_name%
if "%_Path%" NEQ "" (
set "short_path=%drive%%_Path%"
goto :repeat
) else (
set full_name=%drive%\%_FileName%\%full_name%
)
echo %full_name%%name%
endlocal if "%~2" NEQ "" set "%~2=%full_path%%name%"
Not heavy tested yet....
Here is a batch script based on the answer by npocmaka, using the ftp command (together with its sub-command lcd). There you can see that only the last element of a path is expanded to the long name. My idea is now to apply the lcd sub-command for every element of the path individually, so we will get the full names of all elements in the final output.
This script works for directories only. It does not work for files, neither does it work for UNC paths.
So here we go:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ARGS=%*"
set FTP_CMD=lcd
set "TEMP_FILE=%TEMP%\%~n0_%RANDOM%.tmp"
setlocal EnableDelayedExpansion
for %%A in (!ARGS!) do (
endlocal
set "ARG=%%~fA" & set "SEP=\"
setlocal EnableDelayedExpansion
> "%TEMP_FILE%" (
for %%D in ("!ARG:\=" "!") do (
endlocal
if not "%%~D"=="" (
set "ITEM=%%~D"
setlocal EnableDelayedExpansion
echo(%FTP_CMD% "!ITEM!!SEP!"
endlocal
set "SEP="
)
setlocal EnableDelayedExpansion
)
)
set "PREFIX="
for /F "delims=" %%L in ('^< "%TEMP_FILE%" ftp') do (
endlocal
if not defined PREFIX set "PREFIX=%%L"
set "LONG_PATH=%%L"
setlocal EnableDelayedExpansion
)
set "PREFIX=!PREFIX::\.=!" & set "PREFIX=!PREFIX:~,-1!"
for /F "delims=" %%E in ("!PREFIX!") do (
set "LONG_PATH=!LONG_PATH:*%%E=!"
set "LONG_PATH=!LONG_PATH:~,-1!"
)
echo(!LONG_PATH!
)
endlocal
del /Q "%TEMP_FILE%"
endlocal
exit /B
Basically there is a for %%D loop that iterates through all elements of the given path (after it has been expanded to its full path by the outer-most for %%A loop). Each element is enclosed within "" and preceded with lcd (the sub-command of the ftp command to change the local working directory). For the first path element that constitutes a drive, a trailing \ is appended to refer to its root directory. Each of these built path strings is written to a temporary file.
Next the temporary file is redirected into the ftp command, so it changes its local working directory path element by path element. The output of ftp is captured by a for /F %%L loop. Actually the last line of the output is of interest only as this contains the full long path. However, the first line is also stored, where the root directory of the applicable drive is used. This is just needed to easily extract the prefix of the output lines in order to remove it from the output line containing the full path (the ftp command outputs something like Local directory now D:\. on English systems, but I want the script to be language-independent). Finally the said prefix is removed from the full long path and the result is returned on the console.
Here is an improved approach that can also handle paths of files, by handling the last path element in such a case separately by the sub-routine :LAST_ITEM, which does not rely on ftp but on the fact that for loops expand tthe last path element to long paths when wildcards are given:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ARGS=%*"
set FTP_CMD=lcd
set "TEMP_FILE=%TEMP%\%~n0_%RANDOM%.tmp"
setlocal EnableDelayedExpansion
for %%A in (!ARGS!) do (
endlocal
set "ARG=%%~fA" & set "SEP=\" & set "ITEM="
if exist "%%~fA" (
if exist "%%~fA\" (set "FLAG=") else set "FLAG=#"
setlocal EnableDelayedExpansion
> "%TEMP_FILE%" (
for %%D in ("!ARG:\=" "!") do (
endlocal
if not "%%~D"=="" (
set "ITEM=%%~D"
setlocal EnableDelayedExpansion
echo(!FTP_CMD! "!ITEM!!SEP!"
endlocal
set "SEP="
) else set "ITEM="
setlocal EnableDelayedExpansion
)
)
set "PREFIX="
for /F "delims=" %%L in ('^< "%TEMP_FILE%" 2^> nul ftp') do (
endlocal
if not defined PREFIX set "PREFIX=%%L"
set "LONG_PATH=%%L"
setlocal EnableDelayedExpansion
)
set "PREFIX=!PREFIX::\.=!" & set "PREFIX=!PREFIX:~,-1!"
for /F "delims=" %%E in ("!PREFIX!") do (
set "LONG_PATH=!LONG_PATH:*%%E=!"
set "LONG_PATH=!LONG_PATH:~,-1!"
)
if not "!LONG_PATH:~-2!"==":\" set "LONG_PATH=!LONG_PATH!\"
for /F "tokens=1,2 delims=|" %%S in ("!LONG_PATH!|!ITEM!") do (
endlocal
set "LONG_PATH=%%S" & set "ITEM=%%T"
if defined FLAG call :LAST_ITEM ITEM LONG_PATH
setlocal EnableDelayedExpansion
)
if defined FLAG (echo(!LONG_PATH!!ITEM!) else echo(!LONG_PATH!
) else setlocal EnableDelayedExpansion
)
endlocal
del /Q "%TEMP_FILE%"
endlocal
exit /B
:LAST_ITEM var_last_item var_long_path
setlocal EnableDelayedExpansion
for %%I in ("!%~2!!%~1!*") do (
endlocal
set "LONG=%%~nxI" & set "SHORT=%%~snxI"
setlocal EnableDelayedExpansion
if /I "!LONG!"=="!%~1!" (set "%~1=!LONG!"
) else if /I "!SHORT!"=="!%~1!" set "%~1=!LONG!"
)
for /F "delims=" %%T in ("!%~1!") do (
endlocal
set "%~1=%%T"
setlocal EnableDelayedExpansion
)
endlocal
exit /B
My solution:
set shortname=P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL
for /F %f in ('dir /b /s %shortname%') do where /R %~dpf %~nf%~xf
if you use it in a batch file:
for /F %%f in ('dir /b /s %shortname%') do where /R %%~dpf %%~nf%%~xf
Alright, here is a script I began some time ago, relying on the fact that dir /B returns long file or directory names when a wildcard is used. This is a recursive approach that walks up the directory hierarchy of the path given as command line argument and resolves each element. Note that it has problems with paths containing % and/or ^ due to the usage of call:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set ARGS=%*
set "COLL="
setlocal EnableDelayedExpansion
for %%A in (!ARGS!) do (
endlocal
set "ARG=%%~fA"
if exist "%%~fA" (
call :PROC_ITEM COLL "%%~fA" || set "COLL="
)
setlocal EnableDelayedExpansion
)
if defined COLL (echo(!COLL!) else exit /B 1
endlocal
endlocal
exit /B
:PROC_ITEM rtn_built_path val_source_path
setlocal DisableDelayedExpansion
set "FND="
if "%~pnx2"=="\" (
set "COLL=%~d2"
) else (
cd /D "%~dp2." & rem (this must be set before `for /F` in order for `%%~snxJ` to refer to it!)
for /F "delims= eol=|" %%J in ('dir /B /A "%~f2?"') do (
if /I "%%J"=="%~nx2" (
set "FND=\%%J" & rem (this assignment should be executed for long names)
) else (
if /I "%%~snxJ"=="%~nx2" set "FND=\%%J" & rem (and this for short ones)
)
)
if defined FND (
call :PROC_ITEM COLL "%~dp2."
) else (
exit /B 1 & rem (this intercept exceptions and should usually not happen)
)
)
endlocal & set "%~1=%COLL%%FND%"
exit /B

Resources