I'm dealing with a lot of files, mostly .jpg, that need to be batch renamed for ftp use (5k+ per day). Need a batch file to convert filenames to lowercase and replace all spaces and symbols(!#.,;&()) with dashes(-), as an example:
POS!99/6 09.JPG -> pos-99-6-09.jpg
75#0hu dfs*1010.jpg -> 75-0hudfs-1010.jpg
Please, help!
I just tried to set files in lowercase and dont know how to do the rest:
#echo off
setlocal
for /f "Tokens=*" %%f in ('dir /l/b/a-d') do (rename "%%f" "%%f")
for /f "tokens=1,2,* delims=_." %%F in ('dir /b "*_*.*"') do ren "%%F_%%G.%%H" "%%F.%%H"
endlocal
del *.bat
This can do the trick using VBScript Replace RegEx into a batch file :
Note : The pattern to validate a filename is [\\\/:*?\x22<>|], and I added in this class to yours for your request !#.,;&()#
#echo off
Color 0A & Title How to verify if variable contains valid filename in Windows Batch
echo(
Echo Enter filename for this project
set /p "my_filename="
echo(
echo Before Removing the special char the filename is like this : "%my_filename%"
pause
echo(
Call :Remove_Special_Char "%my_filename%" NewFileName
echo After Removing the special char the filename becomes like this : "%NewFileName%"
pause & exit
::-----------------------------------------------------------------------------------
:Remove_Special_Char <String> <Variable to Set>
(
echo WScript.StdOut.WriteLine Search_Replace(Data^)
echo Function Search_Replace(Data^)
echo Dim strPattern, strReplace, strResult,oRegExp
echo Data = wscript.Arguments(0^)
echo strPattern = "[\\\/:*?\x22<>|!#.,;&()# ]"
echo strReplace = "-"
echo Set oRegExp = New RegExp
echo oRegExp.Global = True
echo oRegExp.IgnoreCase = True
echo oRegExp.Pattern = strPattern
echo strResult = oRegExp.Replace(Data,strReplace^)
echo Search_Replace = strResult
echo End Function
)>"%tmp%\%~n0.vbs"
#For /f "delims=" %%i in ('cscript //nologo "%tmp%\%~n0.vbs" "%~1"') do ( Set "%2=%%i" )
If Exist "%tmp%\%~n0.vbs" Del "%tmp%\%~n0.vbs"
Exit /B
::----------------------------------------------------------------------------------
Or using Powershell to replace RegEx into a batch file :
#echo off
Color 0A & Title How to verify if variable contains valid filename in Windows Batch
echo(
Echo Enter filename for this project
set /p "my_filename="
echo(
echo Before Removing the special char the filename is like this : "%my_filename%"
pause
echo(
Call :Remove_Special_Char "%my_filename%" NewFileName
echo After Removing the special char the filename becomes like this : "%NewFileName%"
pause & exit
::-----------------------------------------------------------------------------------
:Remove_Special_Char <String> <Variable to Set>
Set "psCommand='"%~1"' -replace '[\\\/:*?\x22<>|!#.,;&()# ]','-'"
#for /f "usebackq delims=" %%i in (`Powershell -C "%psCommand%"`) do (Set "%2=%%i")
Exit /B
::----------------------------------------------------------------------------------
EDIT :
#echo off
Color 0A & Title Rename
Set "MainFolder=E:\Batch\Stack\Test"
CD /D "%MainFolder%"
#for /f "Tokens=*" %%f in ('dir /l /b /a-d') do (
SetLocal DisableDelayedExpansion
Call :Remove_Special_Char "%%~f" NewVar
SetLocal EnableDelayedExpansion
echo( rename "%%~f" "!NewVar!"
pause
rename "%%~f" "!NewVar!"
)
EndLocal & pause & exit
::-----------------------------------------------------------------------------------
:Remove_Special_Char <String> <Variable to Set>
Set "psCommand=""""%~1"""" -replace '[\\\/:*?\x22<>|!#.,;&()\x20#]','-'"
#for /f "usebackq delims=" %%i in (`Powershell -C "%psCommand%"`) do (Set "%2=%%i")
Exit /B
::----------------------------------------------------------------------------------
I am new to Batch file scripting, I need to pass the parameters to a Batch file function, but not able to do so.
The script is shown below. I am trying to replace the Original text in test.txt to Updated text
set "originalText = Original"
set "updatedText = Updated"
set "sourceLocation = C:\Users\User\Desktop\Temp\Test.txt"
set "targetLocation = C:\Users\User\Desktop\Temp\Test.new"
call:replaceText %originalText% %updatedText% %sourceLocation% %targetLocation%
EXIT /B %ERRORLEVEL%
:replaceText
SETLOCAL
set "replace = %~1"
set "replaced = %~2"
set "source = %~3"
set "target = %~4"
echo %replace%
echo %replaced%
echo %source%
echo %target%
timeout /t 40
setlocal enableDelayedExpansion
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
set "line=%%b"
if defined line set "line=!line:%replace%=%replaced%!"
)
) > %target%
endlocal
DEL source
REN target source
ENDLOCAL
EXIT /B 0
I want to write a batch file to find all .vsdm files and the file name must contain a substring "2.4". But my code is telling me that all my .vsdm files contains the substring "2.4" which is not correct.
FOR /R %completepath% %%G IN (*.vsdm) DO (
set file=%%~nG
If not "%file%"=="%file:2.4=%" (
echo Filename contains 2.4
) else (
echo Filename does NOT contains 2.4
)
)
Can anyone tell me where did I get it wrong?Thanks
If "%file%"=="%file:2.4=%" (
echo Filename "%file%" does NOT contain 2.4
) else (
echo Filename "%file%" contains 2.4
)
Including the filename in the echo may reveal more. I can see no reason for the double-negative approach. The way the code operates may depend on precisely where in code the instructions are located, for instance if these lines are contained within any variety of loop or code-block, operation may depend on other elements, so it's important to present the code in-context and with an example of what was expected and what actually happened.
correct fomatting makes all clear.
There are one or two SO articles about delayed expansion which OP should become familiar with.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R %completepath% %%G IN (*.vsdm) DO (
set "file=%%~nG"
If not "!file!"=="!file:2.4=!" (
echo Filename contains 2.4
) else (
echo Filename does NOT contains 2.4
)
)
ENDLOCAL
You can use the command Where /? that let you use wildcard characters ( ? * ) and UNC paths.
#echo off
Title Find the location of a file with substring by Hackoo
Color 0A
Call :inputbox "Enter the file name to search :" "Enter the file name to search"
If "%input%" == "" Color 0C & (
echo(
echo You must enter a filename to continue with this program
pause>nul & exit
) else (
Call :Browse4Folder "Select the source folder to scan %input%" "c:\scripts"
)
Set "ROOT=%Location%"
::We check whether the input string has an anti-Slach in the end or no ? if yes, we remove it !
IF %ROOT:~-1%==\ SET ROOT=%ROOT:~0,-1%
set whereCmd=where.exe /r %ROOT% %input%
for /f %%a in ('%whereCmd%') do echo %%~nxa --^> %%a
pause & exit
::***************************************************************************
:Browse4Folder
set Location=
set vbs="%temp%\_.vbs"
set cmd="%temp%\_.cmd"
for %%f in (%vbs% %cmd%) do if exist %%f del %%f
for %%g in ("vbs cmd") do if defined %%g set %%g=
(
echo set shell=WScript.CreateObject("Shell.Application"^)
echo set f=shell.BrowseForFolder(0,"%~1",0,"%~2"^)
echo if typename(f^)="Nothing" Then
echo wscript.echo "set Location=Dialog Cancelled"
echo WScript.Quit(1^)
echo end if
echo set fs=f.Items(^):set fi=fs.Item(^)
echo p=fi.Path:wscript.echo "set Location=" ^& p
)>%vbs%
cscript //nologo %vbs% > %cmd%
for /f "delims=" %%a in (%cmd%) do %%a
for %%f in (%vbs% %cmd%) do if exist %%f del /f /q %%f
for %%g in ("vbs cmd") do if defined %%g set %%g=
goto :eof
::***************************************************************************
:InputBox
set "input="
set "heading=%~2"
set "message=%~1"
echo wscript.echo inputbox(WScript.Arguments(0),WScript.Arguments(1)) >"%temp%\input.vbs"
for /f "tokens=* delims=" %%a in ('cscript //nologo "%temp%\input.vbs" "%message%" "%heading%"') do (
set "input=%%a"
)
exit /b
::***************************************************************************
i need batch file to mask the input with * without external file and i need the code to be fast to write letters
FOR EXAMPLE:
#echo off
set char=*
set /p variable=Enter your password:
set char=%variable%
echo %char%
pause
Here is a way to do it using Powershell in a batch file
#echo off
set "psCommand=powershell -Command "$pword = read-host 'Enter Password' -AsSecureString ; ^
$BSTR=[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pword); ^
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)""
for /f "usebackq delims=" %%p in (`%psCommand%`) do set password=%%p
echo %password%
Powershell is installed by default on all newer OS's so it's a good compromise. If you have to support XP machines, you can do something similar with VBScript.
Yes! There is a way in pure batch, with misusing of xcopy and prompt command we can do it
#echo off
setlocal enabledelayedexpansion
call:show "Enter your passwordÿ" "0a"
call:hidePass
set /p password=<_tmp&del _tmp&echo/
echo/%password%
pause>nul
exit
:hidePass
set "str="
set "chars="
:writing
set "key="
for /F "usebackq delims=" %%L in (`xcopy /L /w "%~f0" "%~f0" 2^>NUL`) do (
if not defined key set "key=%%L"
)
setlocal EnableDelayedExpansion
set "key=!key:~-1!"
if "%key%" == "" (echo/!str!>_tmp&exit/b)
call:show "-" "0c"
set "str=!str!!key!"
goto :writing
:show
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "DEL=%%a"
)
call :ColorText %~2 "%~1"
exit/b
:ColorText
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1
exit/b
The only problem is you can not use * as mask, in this case I'm using -
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