batch-file - 从短文件名获取完整路径和长文件名

标签 batch-file

我有一个不错的控制台文件管理器(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"
  • 最佳答案

    以下应该适用于任何有效路径,只要它是 不是 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);
    

    关于batch-file - 从短文件名获取完整路径和长文件名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18654988/

    相关文章:

    batch-file - 从批处理脚本中的字符串中提取前导数字

    java - 无法在一台计算机上运行 jar,但可以在另一台计算机上运行

    batch-file - 无法找出 For 循环中的中间字符串语法

    c++ - 将 *.c/*.h 项目(编译为 C++)转换为 *.cpp/*.hpp

    批处理脚本中的 windows comp 命令 : remove prompts

    windows-7 - 单击 更改时间。 window 里的 bat

    Windows 启动命令无法执行批处理文件

    macos - 相当于 macOS 中的 .bat

    Bash 内联命令的 Windows bat 替代品

    batch-file - 使用管道运算符时出现错误时批处理脚本终止