Get full path and long file name from short file name

前端 未结 10 752
故里飘歌
故里飘歌 2020-12-15 09:13

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 fi

相关标签:
10条回答
  • 2020-12-15 09:22

    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. 
    
    0 讨论(0)
  • 2020-12-15 09:27

    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
    
    0 讨论(0)
  • 2020-12-15 09:29

    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
    
    0 讨论(0)
  • 2020-12-15 09:30

    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 !%=&^.

    0 讨论(0)
  • 2020-12-15 09:30

    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....

    0 讨论(0)
  • 2020-12-15 09:33

    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);
    
    0 讨论(0)
提交回复
热议问题