Разрешить абсолютный путь из относительного пути и / или имени файла
-
22-07-2019 - |
Вопрос
Есть ли способ в пакетном скрипте Windows вернуть абсолютный путь из значения, содержащего имя файла и / или относительный путь?
Данный:
"..\"
"..\somefile.txt"
Мне нужен абсолютный путь относительно пакетного файла.
Пример:
- "somefile.txt" находится в "C:\Foo\"
- "test.bat" находится в разделе "C:\Foo\Bar".
- Пользователь открывает командное окно в "C:\Foo" и вызывает
Bar\test.bat ..\somefile.txt
- В командном файле "C:\Foo\somefile.txt" было бы производным от
%1
Решение
В пакетных файлах, как и в стандартных программах на C, аргумент 0 содержит путь к текущему исполняемому скрипту.Вы можете использовать %~dp0
чтобы получить только часть пути 0-го аргумента (который является текущим скриптом) - этот путь всегда является полным путем.
Вы также можете получить полный путь к вашему первому аргументу, используя %~f1
, но это дает путь в соответствии с текущим рабочим каталогом, что, очевидно, не то, что вы хотите.
Лично я часто использую %~dp0%~1
идиома в моем пакетном файле, которая интерпретирует первый аргумент относительно пути к исполняемому пакету.Однако у него есть один недостаток:это с треском проваливается, если первый аргумент является полным.
Если вам нужно поддерживать оба относительных и абсолютные пути, вы можете использовать Frédéric Ménez's solution:временно измените текущий рабочий каталог.
Вот пример, который продемонстрирует каждый из этих методов:
@echo off
echo %%~dp0 is "%~dp0"
echo %%0 is "%0"
echo %%~dpnx0 is "%~dpnx0"
echo %%~f1 is "%~f1"
echo %%~dp0%%~1 is "%~dp0%~1"
rem Temporarily change the current working directory, to retrieve a full path
rem to the first parameter
pushd .
cd %~dp0
echo batch-relative %%~f1 is "%~f1"
popd
Если вы сохраните это как c: emp\example.bat и запустите его из c:\Users\Public как
c:\Users\Public > emp\example.bat ..\Windows
...вы увидите следующий результат:
%~dp0 is "C:\temp\"
%0 is "\temp\example.bat"
%~dpnx0 is "C:\temp\example.bat"
%~f1 is "C:\Users\windows"
%~dp0%~1 is "C:\temp\..\windows"
batch-relative %~f1 is "C:\Windows"
документацию по набору модификаторов, разрешенных для пакетного аргумента, можно найти здесь:https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/call
Другие советы
Сегодня утром я столкнулся с аналогичной потребностью:как преобразовать относительный путь в абсолютный путь внутри командного скрипта Windows.
Следующее сделало свое дело:
@echo off
set REL_PATH=..\..\
set ABS_PATH=
rem // Save current directory and change to target directory
pushd %REL_PATH%
rem // Save value of CD variable (current directory)
set ABS_PATH=%CD%
rem // Restore original directory
popd
echo Relative path: %REL_PATH%
echo Maps to path: %ABS_PATH%
Большинство из этих ответов кажутся сумасшедшими из-за сложности и супер-ошибок, вот мой - он работает с любой переменной окружения, нет %CD%
или PUSHD
/POPD
, или for /f
ерунда - просто обычные старые пакетные функции.-- Каталог и файл даже не обязательно должны существовать.
CALL :NORMALIZEPATH "..\..\..\foo\bar.txt"
SET BLAH=%RETVAL%
ECHO "%BLAH%"
:: ========== FUNCTIONS ==========
EXIT /B
:NORMALIZEPATH
SET RETVAL=%~dpfn1
EXIT /B
Без необходимости иметь другой пакетный файл для передачи аргументов (и использовать операторы аргументов), вы можете использовать FOR /F
:
FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fi
где находится i
в %%~fi
определена ли переменная в /F %%i
.например.если вы изменили это на /F %%a
тогда последняя часть была бы %%~fa
.
Чтобы сделать то же самое прямо в командной строке (а не в пакетном файле), замените %%
с %
...
Это должно помочь заполнить пробелы в ответе Адриана Плиссона (за который следует проголосовать, как только он его отредактирует ;-):
вы также можете получить полный путь к вашему первому аргументу, используя %~ f1 , но это дает путь в соответствии с текущим путем, что, очевидно, не то, что вы хотите.
к сожалению, я не знаю, как смешать эти два продукта вместе...
Один может справиться %0
и %1
аналогично:
%~dpnx0
для полного определения диска + путь + имя + расширение самого пакетного файла,
%~f0
также достаточно;%~dpnx1
для полного диска + путь + имя + расширение его первого аргумента [если это вообще имя файла],
%~f1
также достаточно;
%~f1
будет работать независимо от того, как вы указали свой первый аргумент:с относительными путями или с абсолютными путями (если вы не указываете расширение файла при присвоении имени %1
, он не будет добавлен, даже если вы используете %~dpnx1
-- однако.
Но как, черт возьми, вы бы назвали файл на другом диске в любом случае, если бы вы не указали эту полную информацию о пути в командной строке в первую очередь?
Однако, %~p0
, %~n0
, %~nx0
и %~x0
может пригодиться, если вас заинтересуют path (без driveletter), filename (без расширения), полное имя файла с расширением или только расширение filename.Но учтите, пока %~p1
и %~n1
будет работать, чтобы узнать путь или имя первого аргумента, %~nx1
и %~x1
не добавит + показать расширение, если только вы уже не использовали его в командной строке.
Вы также можете использовать пакетные функции для этого:
@echo off
setlocal
goto MAIN
::-----------------------------------------------
:: "%~f2" get abs path of %~2.
::"%~fs2" get abs path with short names of %~2.
:setAbsPath
setlocal
set __absPath=%~f2
endlocal && set %1=%__absPath%
goto :eof
::-----------------------------------------------
:MAIN
call :setAbsPath ABS_PATH ..\
echo %ABS_PATH%
endlocal
Небольшое улучшение для Отличное решение BrainSlugs83.Обобщенный, позволяющий присваивать имя выходной переменной среды в вызове.
@echo off
setlocal EnableDelayedExpansion
rem Example input value.
set RelativePath=doc\build
rem Resolve path.
call :ResolvePath AbsolutePath %RelativePath%
rem Output result.
echo %AbsolutePath%
rem End.
exit /b
rem === Functions ===
rem Resolve path to absolute.
rem Param 1: Name of output variable.
rem Param 2: Path to resolve.
rem Return: Resolved absolute path.
:ResolvePath
set %1=%~dpfn2
exit /b
Если убежать от C:\project
результат равен:
C:\project\doc\build
Я не видел много решений этой проблемы.Некоторые решения используют обход каталога с помощью CD, а другие используют пакетные функции.Мое личное предпочтение было отдано пакетным функциям и, в частности, Сделать абсолютным функционируйте так, как предусмотрено подсказками.
Функция имеет некоторые реальные преимущества, в первую очередь в том, что она не изменяет ваш текущий рабочий каталог, а во-вторых, в том, что оцениваемые пути даже не обязательно должны существовать.Вы можете найти несколько полезных советов о том, как использовать эту функцию здесь слишком.
Вот пример скрипта и его выходные данные:
@echo off
set scriptpath=%~dp0
set siblingfile=sibling.bat
set siblingfolder=sibling\
set fnwsfolder=folder name with spaces\
set descendantfolder=sibling\descendant\
set ancestorfolder=..\..\
set cousinfolder=..\uncle\cousin
call:MakeAbsolute siblingfile "%scriptpath%"
call:MakeAbsolute siblingfolder "%scriptpath%"
call:MakeAbsolute fnwsfolder "%scriptpath%"
call:MakeAbsolute descendantfolder "%scriptpath%"
call:MakeAbsolute ancestorfolder "%scriptpath%"
call:MakeAbsolute cousinfolder "%scriptpath%"
echo scriptpath: %scriptpath%
echo siblingfile: %siblingfile%
echo siblingfolder: %siblingfolder%
echo fnwsfolder: %fnwsfolder%
echo descendantfolder: %descendantfolder%
echo ancestorfolder: %ancestorfolder%
echo cousinfolder: %cousinfolder%
GOTO:EOF
::----------------------------------------------------------------------------------
:: Function declarations
:: Handy to read http://www.dostips.com/DtTutoFunctions.php for how dos functions
:: work.
::----------------------------------------------------------------------------------
:MakeAbsolute file base -- makes a file name absolute considering a base path
:: -- file [in,out] - variable with file name to be converted, or file name itself for result in stdout
:: -- base [in,opt] - base path, leave blank for current directory
:$created 20060101 :$changed 20080219 :$categories Path
:$source http://www.dostips.com
SETLOCAL ENABLEDELAYEDEXPANSION
set "src=%~1"
if defined %1 set "src=!%~1!"
set "bas=%~2"
if not defined bas set "bas=%cd%"
for /f "tokens=*" %%a in ("%bas%.\%src%") do set "src=%%~fa"
( ENDLOCAL & REM RETURN VALUES
IF defined %1 (SET %~1=%src%) ELSE ECHO.%src%
)
EXIT /b
И на выходе:
C:\Users\dayneo\Documents>myscript
scriptpath: C:\Users\dayneo\Documents\
siblingfile: C:\Users\dayneo\Documents\sibling.bat
siblingfolder: C:\Users\dayneo\Documents\sibling\
fnwsfolder: C:\Users\dayneo\Documents\folder name with spaces\
descendantfolder: C:\Users\dayneo\Documents\sibling\descendant\
ancestorfolder: C:\Users\
cousinfolder: C:\Users\dayneo\uncle\cousin
Я надеюсь, что это поможет...Это определенно помогло мне :) P.S.Еще раз спасибо DosTips!Ты зажигаешь!
В вашем примере, из Bar est.bat, DIR /B /S ..\somefile.txt вернул бы полный путь.
Вы можете просто объединить их.
SET ABS_PATH=%~dp0
SET REL_PATH=..\SomeFile.txt
SET COMBINED_PATH=%ABS_PATH%%REL_PATH%
это выглядит странно с \..\ в середине вашего пути, но это работает.Не нужно делать ничего сумасшедшего :)
PowerShell в наши дни довольно распространен, поэтому я часто использую его как быстрый способ вызова C #, поскольку в нем есть функции практически для всего:
@echo off
set pathToResolve=%~dp0\..\SomeFile.txt
for /f "delims=" %%a in ('powershell -Command "[System.IO.Path]::GetFullPath( '%projectDirMc%' )"') do @set resolvedPath=%%a
echo Resolved path: %resolvedPath%
Это немного медленно, но полученную функциональность трудно превзойти, если не прибегать к реальному языку сценариев.
решение stijn работает с вложенными папками в C:\Program Files (86)\
,
@echo off
set projectDirMc=test.txt
for /f "delims=" %%a in ('powershell -Command "[System.IO.Path]::GetFullPath( '%projectDirMc%' )"') do @set resolvedPath=%%a
echo full path: %resolvedPath%
Файлы Смотрите все другие ответы
Каталоги
С ..
будучи вашим относительным путем и предполагая, что в данный момент вы находитесь в D:\Projects\EditorProject
:
cd .. & cd & cd EditorProject
(относительный путь)
возвращает абсолютный путь, например
D:\Projects
SET CD=%~DP0
SET REL_PATH=%CD%..\..\build\
call :ABSOLUTE_PATH ABS_PATH %REL_PATH%
ECHO %REL_PATH%
ECHO %ABS_PATH%
pause
exit /b
:ABSOLUTE_PATH
SET %1=%~f2
exit /b