Pregunta

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

Dada :

"..\"
"..\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 comandos 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
¿Fue útil?

Solución

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

También puede obtener la ruta totalmente calificada de su primer argumento utilizando % ~ f1 , pero esto proporciona una ruta de acuerdo con el directorio de trabajo actual, que obviamente no es lo que desea.

Personalmente, a menudo uso el idioma % ~ 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: falla miserablemente si el primer argumento está completamente calificado.

Si necesita admitir rutas absolutas relativas y , puede utilizar Solución de Frédéric Ménez : cambie 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á el siguiente resultado:

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

la documentación para el conjunto de modificadores permitidos en un argumento por lotes se puede encontrar aquí: https://docs.microsoft.com/en- us / windows-server / administration / windows-command / call

Otros consejos

Me encontré con 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 súper defectuosas, aquí está la mía: funciona en cualquier variable de entorno, sin % CD% o PUSHD / POPD , o for / f sin sentido, simplemente las funciones de lote antiguas. - El directorio & amp; el archivo ni siquiera tiene 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 (y usar los operadores de argumentos), 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 cambiara eso a / F %% a , 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 votarse en cuanto la edite ;-):

  

también puede obtener la ruta totalmente calificada de su primer argumento utilizando% ~ f1, pero esto proporciona una ruta de acuerdo con la ruta actual, que obviamente no es lo que desea.

     

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

Uno puede manejar % 0 y % 1 de la misma manera:

  • % ~ dpnx0 para unidad completa + ruta + nombre + extensión del archivo por lotes,
    % ~ f0 también es suficiente;
  • % ~ dpnx1 para unidad completa + 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 especificó su primer argumento: con rutas relativas o con rutas absolutas (si no especifica la extensión del archivo al nombrar % 1 , no se agregará, incluso si usa % ~ dpnx1 , sin embargo.

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

Sin embargo, % ~ p0 , % ~ n0 , % ~ nx0 y % ~ x0 pueden venir útil, si está interesado en la ruta (sin driveletter), 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 encontrar la ruta o el nombre del primer argumento, % ~ nx1 y % ~ x1 no agregará + mostrará la extensión, a menos que ya la haya usado en la línea de comandos.

También puede usar funciones 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

Pequeña mejora en 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 C: \ project la salida es:

C:\project\doc\build

No he visto muchas soluciones a este problema. Algunas soluciones utilizan el recorrido del directorio mediante CD y otras utilizan funciones por lotes. Mi preferencia personal ha sido para las funciones por lotes y, en particular, la MakeAbsolute tal como se proporciona por DosTips.

La función tiene algunos beneficios reales, principalmente porque no cambia su 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 la salida:

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! ¡Eres genial!

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 su ruta 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 script real.

La solución de

stijn funciona con subcarpetas en C: \ Archivos de programa (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 Vea todas las otras respuestas

Directorios

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

cd .. & amp; cd & amp; 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top