gpt4 book ai didi

batch-file - 如何在批处理脚本中将 Windows 短名称路径转换为长名称

转载 作者:行者123 更新时间:2023-12-04 01:58:57 28 4
gpt4 key购买 nike

我正在编写一个 Windows 批处理脚本,我有一个包含使用 8.3 短名称的路径的参数或变量。路径可能代表一个文件或文件夹。

如何将 8.3 路径转换为长名称路径?至少我希望能够简单地打印出完整的长名称路径。但理想情况下,我想安全地将长名称路径放入新变量中。

例如,给定路径 C:\PROGRA~1\AVASTS~1\avast\BROWSE~1.INI ,我要回C:\Program Files\AVAST Software\avast\BrowserCleanup.ini .

作为批处理爱好者,我最感兴趣的是只使用 native Windows 命令的纯批处理解决方案。但是混合使用其他本地脚本工具(如 PowerShell 和 JScript)也是可以接受的。

注:对于这个问题,我发布了我自己的答案。我在网上搜索,惊讶地发现关于这个主题的很少。我制定了多种工作策略,并认为其他人可能会对我的发现感兴趣。

最佳答案

首先我将演示如何转换批处理文件参数 %1并将结果打印到屏幕上。

PowerShell

最简单的解决方案是使用 PowerShell。我在 MSDN blog by Sergey Babkin 上找到了以下代码

$long_path = (Get-Item -LiteralPath $path).FullName

将该代码放入批处理脚本并打印结果是微不足道的:
@echo off
powershell "(Get-Item -LiteralPath '%~1').FullName"

但是,出于两个原因,我尽量避免在批处理中使用 PowerShell
  • PowerShell 不是 XP 原生的
  • PowerShell 的启动时间相当长,因此它使得批处理混合相对较慢

  • CSCRIPT(JScript 或 VBS)

    我在 Computer Hope forum 找到了这个 VBS 片段使用虚拟快捷方式从短格式转换为长格式。
    set oArgs = Wscript.Arguments
    wscript.echo LongName(oArgs(0))
    Function LongName(strFName)
    Const ScFSO = "Scripting.FileSystemObject"
    Const WScSh = "WScript.Shell"
    With WScript.CreateObject(WScSh).CreateShortcut("dummy.lnk")
    .TargetPath = CreateObject(ScFSO).GetFile(strFName)
    LongName = .TargetPath
    End With
    End Function

    我在 Microsoft newsgroup archive 找到了类似的代码和一个旧的 vbscript forum .

    代码只支持文件路径,在批处理中嵌入JScript要容易一些。转换为 JScript 并添加异常处理程序以在文件失败时获取文件夹后,我得到以下混合代码:
    @if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

    ::----------- Batch Code-----------------
    @echo off
    cscript //E:JScript //nologo "%~f0" %1
    exit /b

    ------------ JScript Code---------------*/
    var shortcut = WScript.CreateObject("WScript.Shell").CreateShortcut("dummy.lnk");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var folder='';
    try {
    shortcut.TargetPath = fso.GetFile(WScript.Arguments(0));
    }
    catch(e) {
    try {
    shortcut.TargetPath = fso.GetFolder(WScript.Arguments(0));
    folder='\\'
    }
    catch(e) {
    WScript.StdErr.WriteLine(e.message);
    WScript.Quit(1);
    }
    }
    WScript.StdOut.WriteLine(shortcut.TargetPath+folder);

    纯批次

    令人惊讶的是,我的网络搜索未能找到纯批处理解决方案。所以我就靠自己了。

    如果你知道路径代表一个文件,那么使用 dir /b "yourFilePath" 将 8.3 文件名转换为长名称就很简单了。 .但是,这并不能解析父文件夹的名称。

    如果路径代表文件夹,情况会更糟。仅使用 DIR 命令无法列出特定文件夹 - 它始终列出文件夹的内容而不是文件夹名称本身。

    我尝试了多种策略来处理文件夹路径,但都没有奏效:
  • CD 或 PUSHD 到路径然后查看提示 - 它保留了短文件夹名称
  • 带有/L 和/F 选项的 XCOPY - 它还保留短文件夹名称
  • 参数或 FOR 变量修饰符 %~f1%%~fA - 保留短名称
  • FORFILES - 似乎不支持短名称。

  • 我能想出的唯一解决方案是使用 DIR 迭代转换路径中的每个文件夹,一次一个。这要求我使用 DIR /X /B /AD列出父文件夹中的所有文件夹,包括它们的 8.3 名称,然后使用 FINDSTR 找到正确的短文件夹名称。我依赖这样一个事实,即短文件名总是出现在 <DIR> 之后的完全相同的地方。文本。找到正确的行后,我可以使用变量子字符串或查找/替换操作,或 FOR/F 来解析长文件夹名称。我选择使用 FOR/F。

    我遇到的另一个绊脚石是确定原始路径是代表文件还是文件夹。附加反斜杠和使用 IF EXIST "yourPath\" echo FOLDER 的常用方法如果路径涉及符号链接(symbolic link)或结点(这在公司网络环境中很常见),则错误地将文件报告为文件夹。

    我选择使用 IF EXIST "yourPath\*" ,在 https://stackoverflow.com/a/1466528/1012053 找到.

    但也可以使用 FOR 变量 %%~aF用于查找 d 的属性修饰符(目录)属性,位于 https://stackoverflow.com/a/3728742/1012053 , 和 https://stackoverflow.com/a/8669636/1012053 .

    所以这是一个完全有效的纯批处理解决方案
    @echo off
    setlocal disableDelayedExpansion

    :: Validate path
    set "test=%~1"
    if "%test:**=%" neq "%test%" goto :err
    if "%test:?=%" neq "%test%" goto :err
    if not exist "%test%" goto :err

    :: Initialize
    set "returnPath="
    set "sourcePath=%~f1"

    :: Resolve file name, if present
    if not exist "%~1\*" (
    for /f "eol=: delims=" %%F in ('dir /b "%~1"') do set "returnPath=%%~nxF"
    set "sourcePath=%~f1\.."
    )

    :resolvePath :: one folder at a time
    for %%F in ("%sourcePath%") do (
    if "%%~nxF" equ "" (
    for %%P in ("%%~fF%returnPath%") do echo %%~P
    exit /b 0
    )
    for %%P in ("%sourcePath%\..") do (
    for /f "delims=> tokens=2" %%A in (
    'dir /ad /x "%%~fP"^|findstr /c:"> %%~nxF "'
    ) do for /f "tokens=1*" %%B in ("%%A") do set "returnPath=%%C\%returnPath%"
    ) || set "returnPath=%%~nxF\%returnPath%"
    set "sourcePath=%%~dpF."
    )
    goto :resolvePath

    :err
    >&2 echo Path not found
    exit /b 1

    如果有很多文件夹,用于迭代单个文件夹的 GOTO 会减慢操作速度。如果我真的想优化速度,我可以使用 FOR/F 来调用另一个批处理,并在无限 FOR /L %%N IN () DO... 中解析每个文件夹。循环,并使用 EXIT一旦我到达根部,就跳出循环。但我没有打扰。

    开发可以在变量中返回结果的强大实用程序

    鉴于 ^,有许多边缘情况可能会使健壮脚本的开发复杂化。 , % , 和 !是文件/文件夹名称中的所有合法字符。
  • CALL 双引号 ^人物。这个问题没有很好的解决方案,除了使用变量而不是字符串文字通过引用传递值。如果输入路径仅使用短名称,这不是问题。但是,如果路径混合使用短名称和长名称,则可能会出现问题。
  • 路过%批处理参数中的文字可能很棘手。它可能会让人感到困惑,谁应该多次(如果有的话)将其加倍。同样,在变量中通过引用传递值可能更容易。
  • CALLer 可以从 FOR 循环内调用该实用程序。如果变量或参数包含 % ,然后扩展 %var%%1在实用程序的循环中可能会导致无意的 FOR 变量扩展,因为 FOR 变量在范围内是全局的。该实用程序不得在 FOR 循环内扩展参数,如果使用延迟扩展,则变量只能在 FOR 循环内安全扩展。
  • 包含 ! 的 FOR 变量的扩展如果启用延迟扩展,将会损坏。
  • CALLing 环境可能已启用或禁用延迟扩展。传递包含 ! 的值和 ^跨越 ENDLOCAL 障碍到延迟扩展环境需要引用 !被转义为 ^! .另外,引用 ^必须转义为 ^^ ,但前提是该行包含 ! .当然,如果 CALLing 环境已禁用延迟扩展,则不应转义这些字符。

  • 我已经开发了 JScript 和纯批处理解决方案的强大实用程序形式,它们考虑了上述所有边缘情况。

    默认情况下,实用程序期望路径为字符串文字,但如果 /V,则接受包含路径的变量名称。选项被使用。

    默认情况下,实用程序只是将结果打印到标准输出。但是,如果将返回变量的名称作为额外参数传递,则结果可以在变量中返回。无论您的 CALLing 环境中是否启用或禁用延迟扩展,都保证返回正确的值。

    完整的文档嵌入在实用程序中,可以使用 /? 访问。选项。

    有一些模糊的限制:
  • 返回变量名不能包含 !%字符
  • 同样 /V选项输入变量名不能包含 !%人物。
  • 输入路径不得包含内部双引号。可以将路径用一组双引号括起来,但其中不应包含任何其他引号。

  • 我还没有测试这些实用程序是否在路径名中使用 unicode,或者它们是否使用 UNC 路径。

    jLongPath.bat - 混合 JScript/批处理
    @if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
    :::
    :::jLongPath [/V] SrcPath [RtnVar]
    :::jLongPath /?
    :::
    ::: Determine the absolute long-name path of source path SrcPath
    ::: and return the result in variable RtnVar.
    :::
    ::: If RtnVar is not specified, then print the result to stderr.
    :::
    ::: If option /V is specified, then SrcPath is a variable that
    ::: contains the source path.
    :::
    ::: If the first argument is /?, then print this help to stdout.
    :::
    ::: The returned ERROLEVEL is 0 upon success, 1 if failure.
    :::
    ::: jLongPath.bat version 1.0 was written by Dave Benham
    :::

    ::----------- Batch Code-----------------
    @echo off
    setlocal disableDelayedExpansion
    if /i "%~1" equ "/?" (
    for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A
    exit /b 0
    )
    if /i "%~1" equ "/V" shift /1
    (
    for /f "delims=* tokens=1,2" %%A in (
    'cscript //E:JScript //nologo "%~f0" %*'
    ) do if "%~2" equ "" (echo %%A) else (
    endlocal
    if "!!" equ "" (set "%~2=%%B" !) else set "%~2=%%A"
    )
    ) || exit /b 1
    exit /b 0

    ------------ JScript Code---------------*/
    try {
    var shortcut = WScript.CreateObject("WScript.Shell").CreateShortcut("dummy.lnk"),
    fso = new ActiveXObject("Scripting.FileSystemObject"),
    path=WScript.Arguments(0),
    folder='';
    if (path.toUpperCase()=='/V') {
    var env=WScript.CreateObject("WScript.Shell").Environment("Process");
    path=env(WScript.Arguments(1));
    }
    try {
    shortcut.TargetPath = fso.GetFile(path);
    }
    catch(e) {
    shortcut.TargetPath = fso.GetFolder(path);
    folder='\\'
    }
    var rtn = shortcut.TargetPath+folder+'*';
    WScript.StdOut.WriteLine( rtn + rtn.replace(/\^/g,'^^').replace(/!/g,'^!') );
    }
    catch(e) {
    WScript.StdErr.WriteLine(
    (e.number==-2146828283) ? 'Path not found' :
    (e.number==-2146828279) ? 'Missing path argument - Use jLongPath /? for help.' :
    e.message
    );
    }

    longPath.bat - 纯批次
    :::
    :::longPath [/V] SrcPath [RtnVar]
    :::longPath /?
    :::
    ::: Determine the absolute long-name path of source path SrcPath
    ::: and return the result in variable RtnVar.
    :::
    ::: If RtnVar is not specified, then print the result to stderr.
    :::
    ::: If option /V is specified, then SrcPath is a variable that
    ::: contains the source path.
    :::
    ::: If the first argument is /?, then prints this help to stdout.
    :::
    ::: The returned ERROLEVEL is 0 upon success, 1 if failure.
    :::
    ::: longPath.bat version 1.0 was written by Dave Benham
    :::
    @echo off
    setlocal disableDelayedExpansion

    :: Load arguments
    if "%~1" equ "" goto :noPath
    if "%~1" equ "/?" (
    for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A
    exit /b 0
    )
    if /i "%~1" equ "/V" (
    setlocal enableDelayedExpansion
    if "%~2" equ "" goto :noPath
    if not defined %~2!! goto :notFound
    for /f "eol=: delims=" %%F in ("!%~2!") do (
    endlocal
    set "sourcePath=%%~fF"
    set "test=%%F"
    )
    shift /1
    ) else (
    set "sourcePath=%~f1"
    set "test=%~1"
    )

    :: Validate path
    if "%test:**=%" neq "%test%" goto :notFound
    if "%test:?=%" neq "%test%" goto :notFound
    if not exist "%test%" goto :notFound

    :: Resolve file name, if present
    set "returnPath="
    if not exist "%sourcePath%\*" (
    for /f "eol=: delims=" %%F in ('dir /b "%sourcePath%"') do set "returnPath=%%~nxF"
    set "sourcePath=%sourcePath%\.."
    )

    :resolvePath :: one folder at a time
    for /f "delims=* tokens=1,2" %%R in (^""%returnPath%"*"%sourcePath%"^") do (
    if "%%~nxS" equ "" for %%P in ("%%~fS%%~R") do (
    if "%~2" equ "" (
    echo %%~P
    exit /b 0
    )
    set "returnPath=%%~P"
    goto :return
    )
    for %%P in ("%%~S\..") do (
    for /f "delims=> tokens=2" %%A in (
    'dir /ad /x "%%~fP"^|findstr /c:"> %%~nxS "'
    ) do for /f "tokens=1*" %%B in ("%%A") do set "returnPath=%%C\%%~R"
    ) || set "returnPath=%%~nxS\%%~R"
    set "sourcePath=%%~dpS."
    )
    goto :resolvePath

    :return
    set "delayedPath=%returnPath:^=^^%"
    set "delayedPath=%delayedPath:!=^!%"
    for /f "delims=* tokens=1,2" %%A in ("%delayedPath%*%returnPath%") do (
    endlocal
    if "!!" equ "" (set "%~2=%%A" !) else set "%~2=%%B"
    exit /b 0
    )

    :noPath
    >&2 echo Missing path argument - Use longPath /? for help.
    exit /b 1

    :notFound
    >&2 echo Path not found
    exit /b 1

    关于batch-file - 如何在批处理脚本中将 Windows 短名称路径转换为长名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34473934/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com