我有一个很好的控制台文件管理器(Senh Liu的eXtreme),它将短路径/文件名作为变量传递给menu.bat。
如何生成完整的文件夹名称+长文件名?
示例:
我尝试了以下内容:
SET my_file=%~2
:
echo %my_file%
生成:"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%
生成:"P:\MYPROG~1\SHELLS\zBackup\RefsToMyData.bal"
FOR /F "tokens=* USEBACKQ" %%F IN (`dir /B %2`) DO SET my_file=%%~dpnxF
:
echo %my_file%
生成:"P:\MYPROG~1\SHELLS\zBackup\RefsToMyData.bal"
答案 0 :(得分:5)
简单的解决方案:使用PowerShell。
PS C:\> (Get-Item 'P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL').FullName
P:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal
您可以将PowerShell调用合并到批处理文件中,如下所示:
@echo off
setlocal
for /f "usebackq delims=" %%f in (
`powershell.exe -Command "(Get-Item '%~1').FullName"`
) do @set "var=%%~f"
echo %var%
输出:
C:\> test.cmd P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL
P:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal
PowerShell适用于所有受支持的Windows版本:
如果由于某些原因(例如行政限制)无法使用PowerShell,我会改用VBScript:
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
像上面这样的VBScript可以在这样的批处理文件中使用:
@echo off & setlocal
for /f "delims=" %%f in ('cscript //NoLogo script.vbs "%~1"') do @set "var=%%~f"
echo %var%
但这需要一个额外的脚本文件。
答案 1 :(得分:5)
以下内容适用于任何有效路径,只要 不 为UNC路径即可。路径可以是绝对的或相对的。它可能使用短文件名或长名称(或混合名称)。路径可以指文件夹或文件。
如果结果是文件夹,则结果将以\
结尾,如果文件是文件,则结果不会为\
。
:getLongPath
例程需要一个inputPath变量名作为第一个参数,一个可选的返回变量名作为第二个参数。 inputPath变量应包含有效路径。如果未指定返回变量,则结果将被ECHOed到屏幕(用引号括起来)。如果指定了返回变量,则在变量中返回结果(不带引号)。
只有在返回变量时禁用延迟扩展时才应调用例程。如果在启用延迟扩展的情况下调用,则如果结果包含!
字符,则结果将被破坏。
测试用例(仅适用于我的机器)位于脚本的顶部,实际例程位于底部。
@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"
如果您想运行上述代码,我建议您完全删除@echo off
和:getLongPath
之间的所有测试方案代码。然后,您只需调用脚本,将任何有效路径作为第一个参数传递。因此,应打印正确的长路径。
我很惊讶使用批次解决这个问题有多困难。我认为使用JScript或VBS 并不容易(事实上,Ansgar找到了一个不错的VBS解决方案)。但我喜欢Ansgar的简单PowerShell解决方案 - 这么容易。
<强>更新强>
我发现了一个模糊的情况,如果在FOR循环中调用上述代码失败,并且路径恰好在其中包含FOR变量。它也没有正确地将带有通配符的路径报告为错误,并且当路径包含!
时,它不适用于启用延迟扩展。
所以我在下面创建了一个修改版本。我非常有信心它应该在所有情况下真正起作用,除了UNC路径,并且路径中可能没有unicode。我把它打包成一个易于调用的程序,内置文档。它可以作为独立脚本保留,也可以合并到更大的脚本中。
@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"!
)
我还将Ansgar的VBS改编成混合JScript /批处理脚本。它应该提供与上面的纯批处理脚本相同的结果,但是JScript更容易理解。
@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);
答案 2 :(得分:2)
这将返回完整的长路径名,但取决于:
A)树中没有太多文件(由于时间的原因)
B)树中只有一个目标(长)文件名。
@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%"
使用mybat "d:\MYPROG~1\SHELLS\zBackup\REFSTO~1.BAL"
打电话时
它返回了这个:
"d:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal"
答案 3 :(得分:2)
一个意想不到的简单解决方案:
echo lcd %some_path%|ftp
编辑显示示例:它不是100%
d:\>echo lcd C:\Files\Download\MYMUSI~1\iTunes\ALBUMA~1 |ftp
Local directory now C:\Files\Download\MYMUSI~1\iTunes\Album Artwork.
答案 4 :(得分:1)
@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
路径在开头是硬编码的,但您可以更改它或参数化它。因为您可以轻松获取文件的全名,这只是目录的解决方案。
修改强>
现在适用于文件和目录,并且可以传递参数:
@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%"
和测试输出:
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 --
答案 5 :(得分:1)
这是一个丑陋的批处理作业,我的代码不好,但是粗暴的力量:-)
@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
请在doit
功能中设置您的时间格式(删除适用的格式)。
如果路径或文件名中包含特殊字符,可能会失败,如!%=&^
。
答案 6 :(得分:1)
使用WMIC
和Win32_Directory
进行一次尝试。可能比使用cd
和dir
慢,但当前目录未更改:
@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%"
尚未经过严格测试....
答案 7 :(得分:1)
以下是基于answer npocmaka的批处理脚本,使用ftp
命令(及其子命令lcd
)。在那里,您可以看到只有路径的最后一个元素被扩展为长名称。我的想法是现在分别对路径的每个元素应用lcd
子命令,因此我们将获得最终输出中所有元素的全名。
此脚本仅适用于目录。它不适用于文件,也不适用于UNC路径。
所以我们走了:
@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
基本上有一个for %%D
循环遍历给定路径的所有元素(在它被最外面的for %%A
循环扩展到其完整路径之后)。每个元素都包含在""
中,并以lcd
开头(ftp
命令的子命令用于更改本地工作目录)。对于构成驱动器的第一个路径元素,附加尾随\
以引用其根目录。每个构建的路径字符串都写入临时文件。
接下来,临时文件被重定向到ftp
命令,因此它按路径元素更改其本地工作目录路径元素。 ftp
循环捕获for /F %%L
的输出。实际上输出的最后一行是有意义的,因为它包含完整的长路径。但是,也会存储第一行,其中使用适用驱动器的根目录。这只需要轻松提取输出行的前缀,以便从包含完整路径的输出行中删除它(ftp
命令在英语系统上输出类似Local directory now D:\.
的东西,但我想要脚本与语言无关)。最后,从完整的长路径中删除所述前缀,并在控制台上返回结果。
答案 8 :(得分:0)
我的解决方案:
set shortname=P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL
for /F %f in ('dir /b /s %shortname%') do where /R %~dpf %~nf%~xf
如果在批处理文件中使用它:
for /F %%f in ('dir /b /s %shortname%') do where /R %%~dpf %%~nf%%~xf
答案 9 :(得分:0)
好吧,这是我前一段时间开始的脚本,它依赖于以下事实:使用dir /B
时wildcard返回长文件名或目录名。这是一种递归方法,它遍历作为命令行参数给出的路径的目录层次结构并解析每个元素。请注意,由于使用了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