【问题标题】:Get full path and long file name from short file name从短文件名中获取完整路径和长文件名
【发布时间】:2013-09-06 09:55:33
【问题描述】:

我有一个不错的控制台文件管理器(Senh Liu 的 eXtreme),它将短路径/文件名作为变量传递给 menu.bat。

如何生成完整的文件夹名+长文件名?

例子:

  • 输入变量 = "P:\MYPROG~1\SHELLS\ZBACKUP\REFSTO~1.BAL"
  • 目标变量 = "P:\MyPrograms\SHELLS\zBackup\RefsToMyData.bal"

我尝试了以下方法:

  • 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"

【问题讨论】:

  • SET my_file=%~f2 怎么样?
  • 亲爱的 Vlastimil,对不起,这与 %~2 相同。我知道它会扩展文件名 - 如果你传递一个文件名。但它无法应对“短路径\短文件名”。G
  • 如果只传递一个短文件名,它甚至不起作用。这是一个难以破解的难题 - 好问题
  • 我谦虚地选择了 dbenham 的批处理函数作为我的首选答案。我在一个非常受控的环境中工作,无法访问 Power Shell。感谢大家,

标签: batch-file


【解决方案1】:

简单的解决方案:使用 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 版本:

  • Windows XP SP3 和 Server 2003 SP2:PowerShell v2 available
  • Windows Vista 和 Server 2008:随附 PowerShell v1(默认未安装)、PowerShell v2 available
  • Windows 7 和 Server 2008 R2:预装 PowerShell v2,PowerShell v3 available (batteries not included)
  • Windows 8 和 Server 2012:预装 PowerShell v3

如果由于某种原因(例如管理限制)不能使用 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,只要您可以访问 PowerShell,就可以提供出色的解决方案。
  • 同意,PowerShell 可用于所有现代 Windows,但有时您正在使用的计算机被锁定,您无法安装它。正如所料,PowerShell 解决方案相当简短而优雅。找到一个批处理解决方案是一个野兽!见my answer
  • 我同意,由于管理限制、遗留系统或其他原因,PowerShell 可能无法在所有主机上使用。但是,在这种情况下,我会改用 VBScript。恕我直言,批量执行此操作不值得花时间。不过,我赞扬你的努力 (+1)。
  • 啊 - 非常好的 VBS。 Shell.Application 对象是我找不到的缺失部分。 VBS 可以转换为 JScript 并轻松集成到单个 JScript/批处理混合脚本中。
【解决方案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】:

    以下内容应适用于任何有效路径,只要它不是 UNC 路径即可。路径可以是绝对的或相对的。它可以使用短文件名或长名称(或混合名称)。路径可能指向文件夹或文件。

    如果是文件夹,结果将以\结尾,如果是文件,则以\结尾。

    :getLongPath 例程需要一个 inputPath 变量名作为第一个参数,一个可选的返回变量名作为第二个参数。 inputPath 变量应包含有效路径。如果未指定返回变量,则将结果回显到屏幕(用引号括起来)。如果指定了返回变量,则在变量中返回结果(不带引号)。

    如果您要返回一个变量,则只能在禁用延迟扩展时调用该例程。如果在启用延迟扩展的情况下调用,则如果结果包含 ! 字符,则结果将被破坏。

    测试用例(仅适用于我的机器)位于脚本的顶部,实际的例程位于底部。

    @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%"
    )
    

    === 输出 ===

    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);
    

    【讨论】:

    • 如果路径包含% %s 应该是四倍(我也尝试过 :-))
    • @npocmaka - 很好。我想我有一个解决方案,但这意味着重组例程以通过引用(变量名)而不是字符串常量传递路径。
    • @npocmaka - 我修复了% 问题,并添加了%!& 的测试用例。
    • 这次找不到破解的方法:)
    • @npocmaka -谢谢。使用: 作为 FOR 循环变量有一个微妙的细节,以避免名称中 % 的潜在问题。我选择了:,因为该字符永远不会出现在文件或文件夹名称中。
    【解决方案4】:

    还有一个出人意料的简单解决方案:

     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. 
    

    【讨论】:

    • npocmaka 不完全在那里,但很有趣。请参阅我添加到您的答案中的示例。
    • Scheisse。看起来很有希望。
    • 是的。有趣的不同方法
    • 这种做法的问题是,只有目录路径的最后一个元素会扩展为长名称;然而,我找到了一个解决方案(大约 2.5 年后:-))来克服这个问题——见my answer...
    【解决方案5】:

    这是一个丑陋的批处理作业,我的代码不是很好,但蛮力:-)

    @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 函数中设置您的时间格式(根据适用格式删除)。
    这可能会因路径或文件名中的特殊字符(如!%=&amp;^)而失败。

    【讨论】:

      【解决方案6】:
      @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 --
      

      【讨论】:

      • 我不确定这在哪里失败,但它对我来说无法正常工作。看看我的回答——它使用了类似的策略,但它对我有用:)
      • 我已经修复了一个小错误,如果路径中的最后一项包含空格...,可能会出现该错误,但它适用于我。你收到错误了吗?
      • 如果我提供带有终止 \ 的文件夹路径,那么它会错误地将文件添加到末尾。如果我提供的文件路径在文件名中带有空格,则显示为File Not Found,然后列出父路径的长名称(不包括文件)。
      【解决方案7】:

      使用WMICWin32_Directory 进行一次尝试。可能比使用cddir 慢,但当前目录没有改变:

      @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%"
      

      还没有经过大量测试....

      【讨论】:

        【解决方案8】:

        这是一个基于answer by 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:\. 的内容,但我希望脚本是语言-独立的)。最后,从完整的长路径中删除上述前缀,并在控制台上返回结果。


        这是一种改进的方法,它也可以处理文件的路径,在这种情况下,通过子例程 :LAST_ITEM 单独处理最后一个路径元素,它不依赖于 ftp,而是依赖于 @ 987654340@ 循环在给出通配符时将最后一个路径元素扩展为长路径:

        @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
        

        【讨论】:

          【解决方案9】:

          我的解决方案:

          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
          

          【讨论】:

            【解决方案10】:

            好的,这是我前段时间开始的一个脚本,它依赖于 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
            

            【讨论】:

              猜你喜欢
              • 2011-04-13
              • 1970-01-01
              • 2011-07-10
              • 1970-01-01
              • 1970-01-01
              • 2012-01-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多