Resolver o caminho absoluto do caminho relativo e / ou nome do arquivo

Existe uma maneira em um script em lotes do Windows para retornar um caminho absoluto de um valor que contém um nome de arquivo e / ou caminho relativo?

Dado:

"..\" "..\somefile.txt" 

Eu preciso do caminho absoluto relativo ao arquivo de lote.

Exemplo:

  • “somefile.txt” está localizado em “C: \ Foo \”
  • “test.bat” está localizado em “C: \ Foo \ Bar”.
  • Usuário abre uma janela de comando em “C: \ Foo” e chama Bar\test.bat ..\somefile.txt
  • No arquivo em lote “C: \ Foo \ somefile.txt” seria derivado de %1

Nos arquivos em lote, como nos programas C padrão, o argumento 0 contém o caminho para o script atualmente em execução. Você pode usar %~dp0 para obter apenas a parte do caminho do 0º argumento (que é o script atual) – esse caminho é sempre um caminho completo.

Você também pode obter o caminho completo de seu primeiro argumento usando %~f1 , mas isso fornece um caminho de acordo com o diretório de trabalho atual, que obviamente não é o que você deseja.

Pessoalmente, costumo usar o idioma %~dp0%~1 no meu arquivo de lote, que interpreta o primeiro argumento relativo ao caminho do lote em execução. No entanto, ela tem um defeito: falha miseravelmente se o primeiro argumento for totalmente qualificado.

Se você precisar suportar caminhos relativos e absolutos, você pode usar a solução de Frédéric Ménez : alterar temporariamente o diretório de trabalho atual.

Aqui está um exemplo que demonstrará cada uma destas técnicas:

 @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 

Se você salvar isso como c: \ temp \ example.bat e executá-lo de c: \ Users \ Public as

c: \ Users \ Public> \ temp \ example.bat .. \ windows

… você observará a seguinte saída:

 %~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" 

Eu me deparei com uma necessidade semelhante esta manhã: como converter um caminho relativo em um caminho absoluto dentro de um script de comando do Windows.

O seguinte fez o truque:

 @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% 

A maioria dessas respostas parece louca em vez de complicada e super buggy, aqui está a minha – funciona em qualquer variável de ambiente, sem %CD% ou PUSHD / POPD , ou for /f bobagens – apenas funções de lote antigas. – O diretório e o arquivo nem precisam existir.

 CALL :NORMALIZEPATH "..\..\..\foo\bar.txt" SET BLAH=%RETVAL% ECHO "%BLAH%" :: ========== FUNCTIONS ========== EXIT /B :NORMALIZEPATH SET RETVAL=%~dpfn1 EXIT /B 

Sem ter que ter outro arquivo de lote para passar argumentos para (e usar os operadores de argumentos), você pode usar FOR /F :

 FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fi 

onde o i em %%~fi é a variável definida em /F %%i . por exemplo. Se você alterou para /F %%a , a última parte seria %%~fa .

Para fazer a mesma coisa no prompt de comando (e não em um arquivo de lote), substitua %% por %

Isso é para ajudar a preencher as lacunas na resposta de Adrien Plisson (que deve ser revisto assim que ele for editado ;-):

você também pode obter o caminho completo de seu primeiro argumento usando% ~ f1, mas isso fornece um caminho de acordo com o caminho atual, que obviamente não é o que você deseja.

infelizmente, eu não sei como misturar os 2 juntos …

Pode-se lidar com %0 e %1 mesma forma:

  • %~dpnx0 para unidade totalmente qualificada + caminho + nome + extensão do próprio batchfile,
    %~f0 também é suficiente;
  • %~dpnx1 para unidade totalmente qualificada + caminho + nome + extensão de seu primeiro argumento [se for um nome de arquivo],
    %~f1 também é suficiente;

%~f1 funcionará independente de como você especificou seu primeiro argumento: com caminhos relativos ou com caminhos absolutos (se você não especificar a extensão do arquivo ao nomear %1 , ele não será adicionado, mesmo se você usar %~dpnx1 — Contudo.

Mas como você nomearia um arquivo em uma unidade diferente de qualquer maneira, se você não daria as informações completas sobre o caminho na linha de comando?

Entretanto, %~p0 , %~n0 , %~nx0 e %~x0 podem ser úteis, caso você esteja interessado em path (sem driveletter), filename (sem extensão), filename completo com extensão ou extensão de nome de arquivo apenas. Mas note, enquanto %~p1 e %~n1 irão trabalhar para descobrir o caminho ou o nome do primeiro argumento, %~nx1 e %~x1 não irão adicionar + mostrar a extensão, a menos que você já tenha usado na linha de comando.

Você também pode usar funções em lote para isso:

 @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 

Eu não vi muitas soluções para este problema. Algumas soluções fazem uso da passagem de diretório usando CD e outras fazem uso de funções em lote. Minha preferência pessoal tem sido para funções em lote e, em particular, a function MakeAbsolute fornecida pelo DosTips.

A function tem alguns benefícios reais, principalmente que ela não altera seu diretório de trabalho atual e, em segundo lugar, que os caminhos que estão sendo avaliados nem precisam existir. Você pode encontrar algumas dicas úteis sobre como usar a function aqui também.

Aqui está um script de exemplo e suas saídas:

 @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 

E a saída:

 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 

Espero que isso ajude … Com certeza me ajudou 🙂 PS Obrigado novamente a DosTips! Você é demais!

Pequeno aprimoramento da excelente solução da BrainSlugs83 . Generalizado para permitir nomear a variável do ambiente de saída na chamada.

 @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 

Se executado a partir da saída C:\project :

 C:\project\doc\build 

No seu exemplo, de Bar \ test.bat, DIR / B / S .. \ somefile.txt retornaria o caminho completo.

Você pode apenas concatená-los.

 SET ABS_PATH=%~dp0 SET REL_PATH=..\SomeFile.txt SET COMBINED_PATH=%ABS_PATH%%REL_PATH% 

parece estranho com \ .. \ no meio do caminho, mas funciona. Não precisa fazer nada louco 🙂

O PowerShell é bastante comum nos dias de hoje, então eu o uso frequentemente como uma maneira rápida de invocar o C #, já que ele tem funções para praticamente tudo:

 @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% 

É um pouco lento, mas a funcionalidade adquirida é difícil de superar, a menos que se recorra a uma linguagem de script real.

A solução do stijn trabalha com subpastas em 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% 

Arquivos Veja todas as outras respostas

Diretórios

Com .. sendo seu caminho relativo e supondo que você esteja atualmente em D:\Projects\EditorProject :

cd .. & cd & cd EditorProject (o caminho relativo)

retorna caminho absoluto, por exemplo

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 
Intereting Posts