Risolvi il percorso assoluto dal percorso relativo e / o dal nome del file


155

Esiste un modo in uno script batch di Windows per restituire un percorso assoluto da un valore contenente un nome file e / o percorso relativo?

Dato:

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

Ho bisogno del percorso assoluto relativo al file batch.

Esempio:

  • "somefile.txt" si trova in "C: \ Foo \"
  • "test.bat" si trova in "C: \ Foo \ Bar".
  • L'utente apre una finestra di comando in "C: \ Foo" e chiama Bar\test.bat ..\somefile.txt
  • Nel file batch "C: \ Foo \ somefile.txt" sarebbe derivato %1

1
I percorsi relativi non sono la fine della storia. Considera anche i link simbolici NTFS : molto probabilmente avrai bisogno anche di un analogo realpathper una solida normalizzazione del percorso.
Ulidtko,

Probabilmente non hai bisogno di un percorso esatto! Puoi semplicemente aggiungere un percorso di base: in SET FilePath=%CD%\%1modo che possa essere come C:\Foo\Bar\..\..\some\other\dir\file.txt. I programmi sembrano capire un percorso così complicato.
Fr0sT

Molte di queste risposte sono pazze per complicate, o semplicemente buggy - ma, in realtà, questa è una cosa abbastanza facile da fare in batch, dai un'occhiata alla mia risposta qui sotto .
BrainSlugs83

Risposte:


160

Nei file batch, come nei programmi C standard, l'argomento 0 contiene il percorso dello script attualmente in esecuzione. È possibile utilizzare %~dp0per ottenere solo la parte del percorso del 0 ° argomento (che è lo script corrente) - questo percorso è sempre un percorso completo.

Puoi anche ottenere il percorso completo del tuo primo argomento usando %~f1, ma questo fornisce un percorso in base alla directory di lavoro corrente, che ovviamente non è ciò che desideri.

Personalmente, utilizzo spesso il %~dp0%~1linguaggio nel mio file batch, che interpreta il primo argomento relativo al percorso del batch in esecuzione. Tuttavia ha un difetto: fallisce miseramente se il primo argomento è pienamente qualificato.

Se devi supportare percorsi relativi e assoluti, puoi utilizzare la soluzione di Frédéric Ménez : cambiare temporaneamente la directory di lavoro corrente.

Ecco un esempio che dimostrerà ciascuna di queste tecniche:

@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 lo salvi come c: \ temp \ example.bat e lo esegui da c: \ Users \ Public come

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

... osserverai il seguente output:

%~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 documentazione per l'insieme di modificatori consentiti su un argomento batch è disponibile qui: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/call


4
Puoi gestire %0e %1allo stesso modo: %~dpnx0per percorso completo + nome del file batch stesso, %~dpnx1per percorso completo + nome del suo primo argomento [se questo è un nome di file]. (Ma come diavolo potresti nominare un file su un'unità diversa se non
fornissi

Questo esempio è molto più complesso di @ frédéric-ménez answer
srossross,

3
Questo non riesce sui link simbolici NTFS.
Ulidtko,

@KurtPfeifle d:\foo\..\bar\xyz.txtpuò ancora essere normalizzato. Consiglio di utilizzare questo approccio con la risposta di @ axel-heider di seguito (usando una subroutine batch - quindi puoi farlo su qualsiasi variabile, non solo su una variabile numerata).
BrainSlugs83,

@ulidtko puoi elaborare? Cosa fallisce? - Cosa ti aspetti che accada? - Ti aspetti di passare alla cartella originale? (Perché se lo ha fatto che , direi che un fallimento - è una specie di punto di avere collegamenti simbolici in primo luogo ...)
BrainSlugs83

151

Stamattina ho riscontrato un'esigenza simile: come convertire un percorso relativo in un percorso assoluto all'interno di uno script di comandi di Windows.

Il seguente ha fatto il trucco:

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

2
Questo è DAVVERO utile. Ci sono buone risorse per i trucchi dei file batch come questo?
Danny Parker,

8
Non pensare che sia necessario avere "pushd" seguito da "cd". Puoi semplicemente fare "pushd% REL_PATH%". Ciò salverà la directory corrente e passerà a REL_PATH in una volta sola.
Eddie Sullivan,

1
@DannyParker Un'utile raccolta di tecniche batch e script di esempio è robvanderwoude.com/batchfiles.php . Vedi anche stackoverflow.com/questions/245395/...
HFS

6
@EddieSullivan Se la modifica alla directory fallisce (la directory non esiste, nessuna autorizzazione ...), anche il comando pushd fallisce e in realtà non inserirà un valore nello stack della directory. La chiamata successiva (e tutte le chiamate consecutive) a pop mostrerà il valore sbagliato. L'uso pushd .previene questo problema.
SvenS,

1
Nota che questo non funzionerà su percorsi che sono file o su percorsi inesistenti dove c'è un .. da qualche parte nel mezzo. Non è stato un punto fermo per me, ma è un po 'debole per una soluzione riutilizzabile.
Mihai Danila,

97

La maggior parte di queste risposte sembrano pazze per complicati e super buggy, ecco la mia - funziona su qualsiasi variabile d'ambiente, no %CD%o PUSHD/ POPDo for /fsciocchezze - solo semplici funzioni batch. - La directory e il file non devono nemmeno esistere.

CALL :NORMALIZEPATH "..\..\..\foo\bar.txt"
SET BLAH=%RETVAL%

ECHO "%BLAH%"

:: ========== FUNCTIONS ==========
EXIT /B

:NORMALIZEPATH
  SET RETVAL=%~f1
  EXIT /B

7
Questa è davvero una soluzione pulita, funzionante e diretta.
Razvan,

3
Molto conciso Sto usando questa tecnica ora, ampiamente.
Paulo França Lacerda,

1
Perché è% ~ dpfn1 e non semplicemente% ~ f1? È una sorta di soluzione? % ~ f1 funziona bene per me su Windows 7 e Windows 10 almeno.
il - ya,

1
@ il - ya sembra essere %~f1solo l'abbreviazione di %~dpfn1- quindi, sentitevi liberi di usare %~f1invece. 🙂 - nel formato lungo ~significa rimuovere le virgolette, dsignifica drive, psignifica path, nda solo sarebbe il nome del file senza estensione, ma fnsignifica il nome del file con estensione. la 1intende il primo argomento. - (Credo che questo formato sia precedente a Windows 7.)
BrainSlugs83

1
Ah, mi hai battuto! Secondo un commento nel codice sorgente nt4, file nt4 \ private \ windows \ cmd \ clex.c (datato 27/08/1997), funzione MSCmdVar (), "//% ~ fi - espande% i in un percorso completo name "Mi dispiace per averti infastidito. Speravo di poter arrivare a fondo di questo malinteso. Mi sono imbattuto in% ~ dpfn mentre rivedevo un codice abbastanza recente, mi sono strappato un paio di capelli cercando di capire cosa avrebbe dovuto fare (badate che non me ne sono rimasti molti) e ho iniziato a indagare su come è nato come una vendetta personale.
il - ya

35

Senza dover disporre di un altro file batch per passare argomenti (e utilizzare gli operatori argomento), è possibile utilizzare FOR /F:

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

dove iin %%~fiè la variabile definita in /F %%i. per esempio. se lo cambiassi in /F %%aallora sarebbe l'ultima parte %%~fa.

Per fare la stessa cosa direttamente dal prompt dei comandi (e non in un file batch) sostituire %%con %...


Non cambiare directory è importante, perché rende la ricetta solida per il fatto che il cdcomando non cambia necessariamente la %CD%variabile d'ambiente (se per esempio sei in auto D:e il tuo percorso di destinazione è C:
attivo

@jez Per quello che potresti usarecd /D D:\on.other.drive
Sebastian

1
FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fifallirà se ..\relativePathcontiene spazi
Sebastian

1
Ha funzionato quando ho rimosso il flag / F e usato %% ~ dpfnI. Si noti che for /?suggerisce di utilizzare le lettere maiuscole per i nomi delle variabili, per evitare confusione con i parametri di sostituzione
Sebastian

1
La rimozione di @SebastianGodelet /Fnon funzionerà su percorsi che non esistono e risolverà i caratteri jolly. Se lo vuoi, ottimo, ma se vuoi la funzionalità di /F, allora devi solo usare la tokensparola chiave:FOR /F "tokens=*" %%I IN ("..\relative Path\*") DO echo absolute path: %%~fI
SvenS

15

Questo per aiutare a colmare le lacune nella risposta di Adrien Plisson (che dovrebbe essere votata non appena la modifica ;-):

puoi anche ottenere il percorso completo del tuo primo argomento usando% ~ f1, ma questo fornisce un percorso in base al percorso corrente, che ovviamente non è quello che vuoi.

sfortunatamente, non so come mescolare i 2 insieme ...

Si può gestire %0e %1allo stesso modo:

  • %~dpnx0per drive completo + percorso + nome + estensione del file batch stesso, è
    %~f0anche sufficiente;
  • %~dpnx1per drive completo + percorso + nome + estensione del suo primo argomento [se questo è un nome di file], è
    %~f1anche sufficiente;

%~f1funzionerà indipendentemente da come hai specificato il tuo primo argomento: con percorsi relativi o con percorsi assoluti (se non specifichi l'estensione del file durante la denominazione %1, non verrà aggiunto, anche se usi %~dpnx1- comunque.

Ma come faresti a nominare un file su un disco diverso comunque se non fornissi quelle informazioni sul percorso completo sulla riga di comando in primo luogo?

Tuttavia, %~p0, %~n0, %~nx0e %~x0può rivelarsi utile, si dovrebbe essere interessato a percorso (senza driveletter), il nome del file (senza estensione), nome completo del file con l'estensione o solo l'estensione del nome del file. Ma nota, mentre %~p1e %~n1funzionerà per scoprire il percorso o il nome del primo argomento, %~nx1e %~x1non aggiungerà + mostra l'estensione, a meno che tu non l'abbia già usato sulla riga di comando.


il problema %~dpnx1è che fornisce il percorso completo dell'argomento relativo alla directory corrente , mentre l'OP vuole il percorso relativo alla directory in cui risiede il file batch .
Adrien Plisson,

1
@Adrien Plisson: %~dpnx1fornisce il percorso completo del 1 ° argomento. Non relativa a tutti, e non relativo alla directory corrente sia. Il dè per l'azionamento, il pè per percorso, l' narea e per suffisso sans, il xè per suffisso, il 1è per primo argomento. - E la domanda di Nathan era: "Esiste un modo per restituire un percorso assoluto da un valore contenente un nome file e / o percorso relativo?"
Kurt Pfeifle,

Entrambi, utilizzo dei percorsi assoluti e relativi e fonte con una descrizione . Riconoscente!
it3xl,

10

Puoi anche usare le funzioni batch per questo:

@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

9

Piccolo miglioramento alla soluzione eccellente di BrainSlugs83 . Generalizzato per consentire la denominazione della variabile di ambiente di output nella chiamata.

@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 eseguito C:\projectdall'output è:

C:\project\doc\build

4

Non ho visto molte soluzioni a questo problema. Alcune soluzioni utilizzano l'attraversamento delle directory tramite CD e altre utilizzano le funzioni batch. La mia preferenza personale è stata per le funzioni batch e in particolare la funzione MakeAbsolute fornita da DosTips.

La funzione ha alcuni vantaggi reali, principalmente che non cambia la directory di lavoro corrente e in secondo luogo che i percorsi in fase di valutazione non devono nemmeno esistere. Puoi trovare alcuni suggerimenti utili su come utilizzare la funzione anche qui .

Ecco uno script di esempio e i suoi risultati:

@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 l'output:

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

Spero che questo aiuti ... Di sicuro mi ha aiutato :) PS Grazie ancora a DosTips! Sei forte!


grazie @ulidtko. Non ho mai provato contro symlink prima.
dayneo,

2

Puoi semplicemente concatenarli.

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

sembra strano con \ .. \ nel mezzo del tuo percorso ma funziona. Non c'è bisogno di fare nulla di folle :)


Questo funziona e basta. Può essere ulteriormente semplificato inecho %~dp0..\SomeFile.txt
cowlinator il

1

Nel tuo esempio, da Bar \ test.bat, DIR / B / S .. \ somefile.txt restituisce il percorso completo.


Non è una cattiva idea ... dovrei passare in rassegna il risultato del DIR con un FOR e prendere il primo risultato?
Nathan Taylor,

Ho pensato al problema con più file. Non hai specificato se i multipli fossero un problema, quindi ci sono andato. Per quanto riguarda un ciclo FOR, ho problemi a inserire "../" in un ciclo for, ma ymmv. Se non riesci a farlo funzionare con il comando DIR, puoi reindirizzare l'output su un file e scorrere attraverso quello. Non sto dicendo che il modo "standard" di estrarre il percorso sia negativo, è solo che pensavo che il mio facesse di più di quello che mi hai chiesto. Entrambe le soluzioni fanno "qualcosa" ed entrambe hanno i loro problemi.
George Sisco,

Oh..e, se si esegue il ciclo DIR con successo, è possibile sovrascrivere il reindirizzamento a un file e ottenere l'ultimo risultato. Se inverti l'ordine in un modo che è accettabile per te, puoi ottenere solo il primo risultato. Se è necessario eseguire il ciclo del file, può essere utile invertire l'ordine per ottenere il primo risultato che si trova nell'ultima posizione. Il motivo per cui suggerisco che potrebbe essere più facile ottenere e scartare molte cose piuttosto che affrontarle effettivamente. Non so se il mio modo di pensare abbia senso per te. Metterlo lì fuori come idea.
George Sisco,

Se avete bisogno di normalizzare un percorso di una cartella, vi suggerisco di questa soluzione: stackoverflow.com/questions/48764076/...
uceumern

0

PowerShell è piuttosto comune in questi giorni, quindi lo uso spesso come un modo rapido per invocare C # poiché ha funzioni per praticamente tutto:

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

È un po 'lento, ma la funzionalità acquisita è difficile da battere a meno che senza ricorrere a un vero linguaggio di scripting.


0

la soluzione di stijn funziona con sottocartelle sotto 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%

0

File Vedi tutte le altre risposte

Elenchi

Con ..essere il vostro percorso relativo, e supponendo che si are currently in D:\Projects\EditorProject:

cd .. & cd & cd EditorProject (il percorso relativo)

ritorna percorso assoluto es

D:\Projects


0
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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.