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/1012053 và https://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 !
và ^
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
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
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
@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