2015-12-26 15 views
7

Tôi đang viết tập lệnh batch của Windows và tôi có đối số hoặc biến chứa đường dẫn sử dụng tên ngắn 8,3. Đường dẫn có thể đại diện cho một tệp hoặc thư mục.Làm cách nào để chuyển đổi đường dẫn tên ngắn Windows thành tên dài trong tập lệnh batch

Làm cách nào để chuyển đổi đường dẫn 8,3 thành đường dẫn dài? Ở mức tối thiểu tôi muốn có thể chỉ cần in ra đường dẫn đầy đủ tên dài. Nhưng lý tưởng là tôi muốn một cách an toàn có được đường dẫn tên dài thành một biến mới.

Ví dụ: với đường dẫn C:\PROGRA~1\AVASTS~1\avast\BROWSE~1.INI, tôi muốn trả lại C:\Program Files\AVAST Software\avast\BrowserCleanup.ini.

Là người đam mê hàng loạt, tôi quan tâm nhất đến giải pháp hàng loạt thuần túy chỉ sử dụng các lệnh Windows gốc. Tuy nhiên, việc sử dụng các công cụ tạo tập lệnh gốc khác như PowerShell và JScript cũng được chấp nhận.

Lưu ý:Tôi đang đăng câu trả lời của riêng mình cho câu hỏi này. Tôi đã tìm kiếm trên web và rất ngạc nhiên khi tìm thấy chút ít quý giá về chủ đề này. Tôi đã phát triển nhiều chiến lược làm việc và nghĩ rằng những người khác có thể quan tâm đến những gì tôi đã tìm thấy.

Trả lời

10

Trước tiên, tôi sẽ trình bày cách chuyển đổi đối số tệp lô %1 và in kết quả vào màn hình.

PowerShell

Giải pháp đơn giản nhất là sử dụng PowerShell. Tôi thấy đoạn mã sau vào một MSDN blog by Sergey Babkin

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

Đưa mã mà trong một kịch bản hàng loạt và in kết quả là tầm thường:

@echo off 
powershell "(Get-Item -LiteralPath '%~1').FullName" 

Tuy nhiên, tôi cố gắng tránh sử dụng PowerShell trong hàng loạt vì hai lý do

  • PowerShell không có nguồn gốc từ XP
  • Thời gian khởi động cho PowerShell là đáng kể, do đó, nó làm cho lai mẻ tương đối chậm


cscript (JScript hoặc VBS)

tôi thấy VBS này đoạn mã tại Computer Hope forum có sử dụng một phím tắt giả để chuyển đổi từ ngắn mẫu dài.

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 

Tôi tìm thấy mã tương tự tại số Microsoft newsgroup archive và cũ vbscript forum.

Mã chỉ hỗ trợ đường dẫn tệp và việc nhúng JScript trong hàng loạt dễ dàng hơn một chút.Sau khi chuyển đổi để JScript và thêm một xử lý ngoại lệ để có được một thư mục nếu một tập tin thất bại, tôi lấy mã lai sau:

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


tinh khiết hàng loạt

Đáng ngạc nhiên, tìm kiếm web của tôi thất bại trong việc tìm ra một giải pháp hàng loạt tinh khiết. Vì vậy, tôi đã tự mình làm.

Nếu bạn biết rằng đường dẫn đại diện cho một tệp, thì việc chuyển đổi tên tệp 8,3 thành tên dài bằng cách sử dụng dir /b "yourFilePath" là một vấn đề đơn giản. Tuy nhiên, điều đó không giải quyết tên của thư mục gốc (s).

Tình hình thậm chí còn tồi tệ hơn nếu đường dẫn đại diện cho một thư mục. Không có cách nào để liệt kê một thư mục cụ thể chỉ sử dụng lệnh DIR - nó luôn liệt kê các nội dung của thư mục thay vì tên thư mục.

Tôi đã thử một số chiến lược để xử lý các đường dẫn thư mục, và không ai trong số họ làm việc:

  • CD hoặc pushd đến đường dẫn và sau đó nhìn vào dấu nhắc - nó bảo tên thư mục ngắn
  • xcopy với/L và/tùy chọn F - nó cũng giữ gìn những cái tên ngắn thư mục
  • Đối số hoặc cHO biến modifier %~f1 hoặc %%~fA - giữ gìn tên ngắn
  • FORFILES - không xuất hiện để hỗ trợ tên ngắn.

Giải pháp duy nhất tôi có thể đưa ra là sử dụng DIR để chuyển đổi từng thư mục trong đường dẫn, mỗi lần một. Điều này yêu cầu tôi sử dụng DIR /X /B /AD để liệt kê tất cả các thư mục trong thư mục mẹ, bao gồm tên 8.3 của chúng và sau đó sử dụng hàm FINDSTR để tìm đúng tên thư mục ngắn. Tôi dựa vào thực tế là tên tệp ngắn luôn xuất hiện ở cùng một vị trí chính xác sau văn bản <DIR>. Khi tôi xác định đúng dòng, tôi có thể sử dụng chuỗi con biến hoặc tìm/thay thế các hoạt động hoặc FOR/F để phân tích cú pháp tên thư mục dài. Tôi đã chọn sử dụng FOR/F.

Một trở ngại khác mà tôi có là xác định xem đường dẫn ban đầu có biểu thị một tệp hay thư mục không. Cách tiếp cận thường được sử dụng để thêm dấu gạch chéo ngược và sử dụng IF EXIST "yourPath\" echo FOLDER báo cáo không đúng một tệp dưới dạng thư mục nếu đường dẫn liên quan đến liên kết tượng trưng hoặc đường giao nhau, điều này thường gặp trong môi trường mạng công ty.

Tôi đã chọn sử dụng IF EXIST "yourPath\*", được tìm thấy tại https://stackoverflow.com/a/1466528/1012053.

Nhưng cũng có thể sử dụng biến CHO %%~aF biến tố thuộc tính để tìm thuộc tính d (thư mục), được tìm thấy tại https://stackoverflow.com/a/3728742/1012053https://stackoverflow.com/a/8669636/1012053.

Vì vậy, đây là một giải pháp hàng loạt tinh khiết hoàn toàn làm việc

@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 

Các GOTO sử dụng để lặp các thư mục cá nhân sẽ làm chậm hoạt động xuống nếu có nhiều thư mục. Nếu tôi thực sự muốn tối ưu hóa tốc độ, tôi có thể sử dụng FOR/F để gọi một quy trình khác, và giải quyết từng thư mục trong một vòng lặp vô hạn FOR /L %%N IN() DO... và sử dụng EXIT để thoát khỏi vòng lặp khi tôi đến gốc. Nhưng tôi không bận tâm.


Devoloping tiện ích mạnh mẽ mà có thể trở lại kết quả trong một biến

Có một số trường hợp cạnh đó có thể làm phức tạp phát triển của một kịch bản mạnh mẽ cho rằng ^, %, và ! đều ký tự pháp lý trong tên tệp/thư mục.

  • CALL tăng gấp đôi được trích dẫn ^ ký tự. Không có giải pháp tốt cho vấn đề này, ngoài việc vượt qua giá trị bằng cách tham chiếu bằng cách sử dụng một biến thay vì như một chuỗi ký tự. Đây không phải là vấn đề nếu đường dẫn đầu vào chỉ sử dụng tên ngắn. Nhưng nó có thể là một vấn đề nếu đường dẫn sử dụng một hỗn hợp của tên ngắn và dài.

  • Chuyển % literals trong đối số lô có thể khó khăn. Nó có thể gây nhầm lẫn với những người nhiều lần (nếu ở tất cả) nó phải được tăng gấp đôi. Một lần nữa nó có thể dễ dàng hơn để truyền giá trị bằng cách tham chiếu trong một biến.

  • Trình chặn có thể gọi tiện ích từ trong vòng lặp FOR. Nếu một biến hoặc đối số chứa %, thì việc mở rộng %var% hoặc %1 trong vòng lặp trong tiện ích có thể dẫn đến vô ý FOR exansion biến vì các biến FOR là phạm vi toàn cục. Tiện ích không được mở rộng các đối số trong vòng lặp FOR và các biến chỉ có thể được mở rộng một cách an toàn trong vòng lặp FOR nếu sử dụng mở rộng trễ.

  • Mở rộng biến FOR có chứa ! sẽ bị hỏng nếu mở rộng chậm trễ được bật.

  • Môi trường GỌI có thể đã bị trì hoãn mở rộng hoặc vô hiệu hóa. Việc chuyển các giá trị có chứa !^ qua hàng rào ENDLOCAL tới môi trường mở rộng bị trì hoãn yêu cầu được trích dẫn là ! được thoát là ^!. Ngoài ra, được trích dẫn ^ phải được thoát là ^^, nhưng chỉ khi dòng có chứa !. Tất nhiên những ký tự đó không nên được thoát nếu môi trường CALLing bị trì hoãn mở rộng bị vô hiệu hóa.

tôi đã phát triển các hình thức tiện ích mạnh mẽ của cả hai JScript và giải pháp hàng loạt tinh khiết mà đưa vào tài khoản tất cả các trường hợp cạnh trên.

Tiện ích mong đợi đường dẫn dưới dạng chuỗi theo mặc định, nhưng chấp nhận tên biến chứa đường dẫn nếu tùy chọn /V được sử dụng.

Theo mặc định, các bản in chỉ cần in kết quả vào chế độ xuất chuẩn. Nhưng kết quả có thể được trả về trong một biến nếu bạn chuyển tên của biến trả về làm đối số bổ sung. Giá trị chính xác được đảm bảo sẽ được trả về, bất kể việc mở rộng trễ có được bật hay tắt trong môi trường CALLing của bạn hay không.

Tài liệu đầy đủ được nhúng trong các tiện ích và có thể được truy cập bằng cách sử dụng tùy chọn /?.

Có một vài hạn chế tối nghĩa:

  • Sự trở lại tên biến không được chứa ! hoặc % ký tự
  • Tương tự như vậy /V tùy chọn đầu vào tên biến không được chứa ! hoặc % ký tự.
  • Đường dẫn nhập không được chứa dấu ngoặc kép bên trong. Nó là OK cho con đường được kèm theo witin một bộ ngoặc kép, nhưng không nên có bất kỳ dấu ngoặc kép bổ sung nào bên trong.

Tôi chưa kiểm tra xem các tiện ích có hoạt động với unicode trong tên đường dẫn hoặc nếu chúng hoạt động với đường dẫn UNC.


jLongPath.bat - lai JScript/mẻ

@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 - batch tinh khiết

::: 
:::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 
+0

Không bao giờ có vấn đề này nhưng tốt để biết rằng có một số giải pháp , Cảm ơn ! – SachaDee

+0

Tôi đoán dòng 31 của 'longPath.bat' nên đọc' nếu không được xác định!% ~ 2! ', Phải không? – aschipfl

+1

@aschipfl - Không, cú pháp kỳ lạ là cố ý. Tôi cần câu lệnh IF để có cú pháp hợp lệ khi '% ~ 2' không xác định. '!!' có khi dòng được phân tích cú pháp, vì vậy điều kiện IF là hợp lệ bất kể. Sau đó, khi thực hiện '!!' trở thành không có gì bởi vì mở rộng trễ được kích hoạt. – dbenham

Các vấn đề liên quan