Resolver la ruta absoluta desde la ruta relativa y / o el nombre del archivo

¿Hay alguna forma en una secuencia de comandos por lotes de Windows para devolver una ruta absoluta desde un valor que contiene un nombre de archivo y / o una ruta relativa?

Dado:

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

Necesito la ruta absoluta relativa al archivo por lotes.

Ejemplo:

  • “somefile.txt” se encuentra en “C: \ Foo \”
  • “test.bat” se encuentra en “C: \ Foo \ Bar”.
  • El usuario abre una ventana de comando en “C: \ Foo” y llama a Bar\test.bat ..\somefile.txt
  • En el archivo por lotes, “C: \ Foo \ somefile.txt” se derivaría de %1

En los archivos por lotes, como en los progtwigs estándar C, el argumento 0 contiene la ruta al script en ejecución. Puede usar %~dp0 para obtener solo la parte de ruta del 0º argumento (que es el script actual); esta ruta es siempre una ruta totalmente calificada.

También puede obtener la ruta completa de su primer argumento utilizando %~f1 , pero esto le da una ruta de acuerdo con el directorio de trabajo actual, que obviamente no es lo que quiere.

Personalmente, a menudo uso la %~dp0%~1 en mi archivo por lotes, que interpreta el primer argumento relativo a la ruta del lote en ejecución. Sin embargo, tiene una deficiencia: fracasa rotundamente si el primer argumento está totalmente calificado.

Si necesita admitir rutas relativas y absolutas, puede utilizar la solución de Frédéric Ménez : cambiar temporalmente el directorio de trabajo actual.

Aquí hay un ejemplo que demostrará cada una de estas 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 

Si guarda esto como c: \ temp \ example.bat y lo ejecuta desde c: \ Users \ Public como

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

… observará la siguiente salida:

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

Encontré una necesidad similar esta mañana: cómo convertir una ruta relativa en una ruta absoluta dentro de un script de comando de Windows.

Lo siguiente hizo el truco:

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

La mayoría de estas respuestas parecen locas por complicadas y super buggy, aquí está la mía: funciona en cualquier variable de entorno, no %CD% o PUSHD / POPD , o for /f absurdo, simplemente funciones antiguas de lotes. – El directorio y el archivo ni siquiera tienen que existir.

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

Sin tener que tener otro archivo por lotes para pasar argumentos a (y usar los operadores de argumento), puede usar FOR /F :

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

donde i en %%~fi es la variable definida en /F %%i . p.ej. si cambió eso a /F %%a entonces la última parte sería %%~fa .

Para hacer lo mismo en el símbolo del sistema (y no en un archivo por lotes) reemplace %% con %

Esto es para ayudar a llenar los vacíos en la respuesta de Adrien Plisson (que debe ser actualizada tan pronto como lo edite ;-):

también puede obtener la ruta completa de su primer argumento usando% ~ f1, pero esto le da una ruta de acuerdo con la ruta actual, que obviamente no es lo que quiere.

desafortunadamente, no sé cómo mezclar los 2 juntos …

Uno puede manejar %0 y %1 mismo modo:

  • %~dpnx0 para drive + ruta + nombre + extensión totalmente calificada del archivo por lotes,
    %~f0 también es suficiente;
  • %~dpnx1 para unidad totalmente calificada + ruta + nombre + extensión de su primer argumento [si es un nombre de archivo],
    %~f1 también es suficiente;

%~f1 funcionará independientemente de cómo hayas especificado tu primer argumento: con rutas relativas o con rutas absolutas (si no especificas la extensión del archivo al nombrar %1 , no se agregará, incluso si usas %~dpnx1 — sin embargo.

Pero ¿cómo demonios nombrarías un archivo en una unidad diferente de todos modos si no le dieras esa información de ruta completa en la línea de comandos en primer lugar?

Sin embargo, %~p0 , %~n0 , %~nx0 y %~x0 pueden ser útiles, si está interesado en la ruta (sin letra de unidad), nombre de archivo (sin extensión), nombre de archivo completo con extensión o extensión de nombre de archivo solamente. Pero tenga en cuenta que mientras %~p1 y %~n1 trabajarán para descubrir la ruta o el nombre del primer argumento, %~nx1 y %~x1 no agregarán + mostrarán la extensión, a menos que ya la hayan utilizado en la línea de comandos.

También puede usar funciones de proceso por lotes para esto:

 @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 

No he visto muchas soluciones a este problema. Algunas soluciones hacen uso del recorrido de directorio usando CD y otras hacen uso de funciones de proceso por lotes. Mi preferencia personal ha sido para las funciones por lotes y, en particular, la función MakeAbsolute proporcionada por DosTips.

La función tiene algunos beneficios reales, principalmente que no cambia el directorio de trabajo actual y, en segundo lugar, que las rutas que se evalúan ni siquiera tienen que existir. Puede encontrar algunos consejos útiles sobre cómo usar la función aquí también.

Aquí hay un script de ejemplo y sus resultados:

 @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 

Y el resultado:

 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 esto ayude … Seguro que me ayudó 🙂 PD ¡Gracias de nuevo a DosTips! ¡Rock!

Pequeña mejora a la excelente solución de BrainSlugs83 . Generalizado para permitir nombrar la variable de entorno de salida en la llamada.

 @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 

Si se ejecuta desde la salida C:\project es:

 C:\project\doc\build 

En su ejemplo, desde Bar \ test.bat, DIR / B / S .. \ somefile.txt devolvería la ruta completa.

Puedes concatenarlos.

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

parece extraño con \ .. \ en el medio de tu camino, pero funciona. No hay necesidad de hacer nada loco 🙂

PowerShell es bastante común en estos días, así que lo uso a menudo como una forma rápida de invocar C #, ya que tiene funciones para casi todo:

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

Es un poco lento, pero la funcionalidad obtenida es difícil de superar a menos que no se recurra a un lenguaje de scripting real.

La solución de stijn funciona con subcarpetas en 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% 

Archivos Ver todas las demás respuestas

Directorios

Con .. ser su ruta relativa, y suponiendo que se encuentre actualmente en D:\Projects\EditorProject :

cd .. & cd & cd EditorProject (la ruta relativa)

devuelve la ruta absoluta, por ejemplo

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