Lösen Sie den absoluten Pfad vom relativen Pfad und / oder Dateinamen auf

154

Gibt es in einem Windows-Batch-Skript eine Möglichkeit, einen absoluten Pfad von einem Wert zurückzugeben, der einen Dateinamen und / oder einen relativen Pfad enthält?

Gegeben:

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

Ich brauche den absoluten Pfad relativ zur Batch-Datei.

Beispiel:

  • "somefile.txt" befindet sich in "C: \ Foo \"
  • "test.bat" befindet sich in "C: \ Foo \ Bar".
  • Der Benutzer öffnet ein Befehlsfenster in "C: \ Foo" und ruft auf Bar\test.bat ..\somefile.txt
  • In der Batch-Datei würde "C: \ Foo \ somefile.txt" von abgeleitet %1
Nathan Taylor
quelle
1
Relative Pfade sind nicht das Ende der Geschichte. Berücksichtigen Sie auch NTFS-Symlinks : Wahrscheinlich benötigen Sie auch ein Analogon realpathfür eine robuste Pfadnormalisierung.
Ulidtko
Wahrscheinlich brauchen Sie überhaupt keinen genauen Weg! Sie können einfach einen Basispfad hinzufügen: SET FilePath=%CD%\%1so könnte es sein C:\Foo\Bar\..\..\some\other\dir\file.txt. Programme scheinen einen so komplizierten Weg zu verstehen.
Fr0sT
Viele dieser Antworten sind verrückt nach komplizierten oder einfach nur fehlerhaften - aber das ist eigentlich eine ziemlich einfache Sache im Stapel, werfen Sie einen Blick auf meine Antwort unten .
BrainSlugs83

Antworten:

158

In Batch-Dateien enthält Argument 0 wie in Standard-C-Programmen den Pfad zum aktuell ausgeführten Skript. Sie können %~dp0nur den Pfadabschnitt des 0. Arguments abrufen (dies ist das aktuelle Skript). Dieser Pfad ist immer ein vollständig qualifizierter Pfad.

Sie können auch den vollständig qualifizierten Pfad Ihres ersten Arguments mithilfe von ermitteln %~f1. Dies gibt jedoch einen Pfad entsprechend dem aktuellen Arbeitsverzeichnis an, der offensichtlich nicht Ihren Wünschen entspricht.

Persönlich verwende ich häufig das %~dp0%~1Idiom in meiner Batch-Datei, das das erste Argument relativ zum Pfad des ausführenden Batches interpretiert. Es hat jedoch ein Manko: Es scheitert kläglich, wenn das erste Argument voll qualifiziert ist.

Wenn Sie sowohl relative als auch absolute Pfade unterstützen müssen, können Sie die Lösung von Frédéric Ménez verwenden : Ändern Sie vorübergehend das aktuelle Arbeitsverzeichnis.

Hier ist ein Beispiel, das jede dieser Techniken demonstriert:

@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

Wenn Sie dies als c: \ temp \ example.bat speichern und unter c: \ Users \ Public as ausführen

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

... Sie werden die folgende Ausgabe beobachten:

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

Die Dokumentation zu den Modifikatoren, die für ein Batch-Argument zulässig sind, finden Sie hier: https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/call

Adrien Plisson
quelle
4
Sie können handhaben %0und %1ebenfalls: %~dpnx0für vollqualifizierten Pfad + Name der Batch - Datei selbst, %~dpnx1für den vollqualifizierten Pfad + Name seines ersten Argument [ , ob das eine Dateiname bei allen]. (Aber wie um alles in der Welt würden Sie eine Datei auf einem anderen Laufwerk benennen, wenn Sie diese vollständigen Pfadinformationen sowieso nicht in der Befehlszeile angeben würden?)
Kurt Pfeifle
Dieses Beispiel ist weitaus komplexer als die Antwort von @ frédéric-ménez
srossross
3
Dies schlägt bei NTFS-Symlinks fehl.
Ulidtko
@KurtPfeifle d:\foo\..\bar\xyz.txtkann noch normalisiert werden. Ich empfehle, diesen Ansatz mit der folgenden Antwort von @ axel-heider zu verwenden (unter Verwendung einer Batch-Subroutine - dann können Sie dies für jede Variable tun, nicht nur für eine nummerierte Variable).
BrainSlugs83
@ulidtko kannst du das näher erläutern? Was scheitert? - Was erwartest du? - Erwarten Sie eine Auflösung in den ursprünglichen Ordner? (Denn wenn es so wäre , dass , würde ich rufen , dass ein Ausfall - es ist eine Art des Punktes von Symlinks in erster Linie mit ...)
BrainSlugs83
151

Ich bin heute Morgen auf ein ähnliches Bedürfnis gestoßen: wie man einen relativen Pfad in einen absoluten Pfad innerhalb eines Windows-Befehlsskripts konvertiert.

Folgendes hat den Trick gemacht:

@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%
Frédéric Ménez
quelle
2
Dies ist wirklich nützlich. Gibt es gute Ressourcen für solche Batch-Datei-Tricks?
Danny Parker
8
Denken Sie nicht, dass es notwendig ist, "pushd" gefolgt von "cd" zu haben. Sie können einfach "pushd% REL_PATH%" ausführen. Dadurch wird das aktuelle Verzeichnis gespeichert und auf einmal zu REL_PATH gewechselt.
Eddie Sullivan
1
@DannyParker Eine nützliche Sammlung von Batch-Techniken und Beispielskripten ist robvanderwoude.com/batchfiles.php . Siehe auch stackoverflow.com/questions/245395/…
hfs
6
@EddieSullivan Wenn das Wechseln in das Verzeichnis fehlschlägt (Verzeichnis existiert nicht, keine Berechtigung ...), schlägt der Befehl pushd ebenfalls fehl und überträgt keinen Wert auf den Verzeichnisstapel. Beim nächsten Aufruf (und allen aufeinander folgenden Aufrufen) von popd wird der falsche Wert angezeigt. Die Verwendung pushd .verhindert dieses Problem.
SvenS
1
Beachten Sie, dass dies nicht für Pfade funktioniert, die Dateien sind, oder für nicht vorhandene Pfade, in denen sich irgendwo in der Mitte ein .. befindet. Es war kein Show Stopper für mich, aber es ist eine kleine Schwäche für eine wiederverwendbare Lösung.
Mihai Danila
97

Die meisten dieser Antworten scheinen verrückt nach komplizierten und super fehlerhaften zu sein. Hier ist meine - sie funktioniert mit jeder Umgebungsvariablen, ohne %CD%oder PUSHD/ POPDoder for /fUnsinn - nur mit alten Batch-Funktionen. - Das Verzeichnis und die Datei müssen nicht einmal vorhanden sein.

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

ECHO "%BLAH%"

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

:NORMALIZEPATH
  SET RETVAL=%~dpfn1
  EXIT /B
BrainSlugs83
quelle
7
Dies ist in der Tat eine saubere, funktionierende und unkomplizierte Lösung.
Razvan
3
Sehr prägnant. Ich benutze diese Technik jetzt weit verbreitet.
Paulo França Lacerda
Warum ist es% ~ dpfn1 und nicht einfach% ~ f1? Ist es eine Art Problemumgehung? % ~ f1 funktioniert für mich mindestens unter Windows 7 und Windows 10 einwandfrei.
il - ya
1
@ il - ya sieht so aus, als wäre %~f1es nur eine Abkürzung für %~dpfn1- also zögern Sie nicht, es %~f1stattdessen zu verwenden . 🙂 - im Langformat ~bedeutet das Entfernen der Anführungszeichen, dbedeutet Laufwerk, pbedeutet Pfad, nallein wäre Dateiname ohne Erweiterung, fnbedeutet aber Dateiname mit Erweiterung. das 1bedeutet das erste Argument. - (Ich glaube, dieses Format ist älter als Windows 7.)
BrainSlugs83
@ BrainSlugs83 Diese Variablen sind seit XP verfügbar (in cmd.exe), aber ihre Verwendung hat sich seitdem nicht geändert:% ~ f1 - erweitert% 1 zu einem vollständig qualifizierten Pfadnamen; % ~ d1 - erweitert% 1 nur zu einem Laufwerksbuchstaben; % ~ p1 - erweitert% 1 nur zu einem Pfad; % ~ n1 - erweitert% 1 nur auf einen Dateinamen; % ~ x1 - erweitert% 1 nur um eine Dateierweiterung. Es gibt keinen Sonderfall für% ~ fn1, es wird kein Name + Erweiterung erzeugt, der Schalter f hat einfach Vorrang vor d, p, n und x, wenn sie kombiniert werden. Es scheint keinen guten Grund zu geben, d, p und n hinzuzufügen.
il - ya
35

Ohne dass Sie eine andere Batch-Datei benötigen, um Argumente zu übergeben (und die Argumentoperatoren zu verwenden), können Sie Folgendes verwenden FOR /F:

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

Dabei ist das iin %%~fidie unter definierte Variable /F %%i. z.B. Wenn Sie das ändern /F %%awürden, wäre der letzte Teil %%~fa.

Um dasselbe direkt an der Eingabeaufforderung (und nicht in einer Batch-Datei) zu tun, ersetzen Sie %%durch %...

Peter Ritchie
quelle
Es ist wichtig, das Verzeichnis nicht zu ändern, da das Rezept dadurch robust wird, dass der cdBefehl die %CD%Umgebungsvariable nicht unbedingt ändert (wenn Sie beispielsweise auf dem Laufwerk sind D:und Ihr C:
Zielpfad
@jez Dafür könnten Sie verwendencd /D D:\on.other.drive
Sebastian
FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fiwird fehlschlagen, wenn das ..\relativePathLeerzeichen enthält
Sebastian
1
Es hat funktioniert, als ich das / F-Flag entfernt und %% ~ dpfnI verwendet habe. Beachten Sie, for /?dass die Verwendung von Großbuchstaben für Variablennamen empfohlen wird, um Verwechslungen mit den Substitutionsparametern zu vermeiden
Sebastian
1
Das Entfernen von @SebastianGodelet /Ffunktioniert nicht für nicht vorhandene Pfade und löst Platzhalterzeichen auf. Wenn Sie das wollen, großartig, aber wenn Sie die Funktionalität von wollen /F, dann müssen Sie nur das tokensSchlüsselwort verwenden:FOR /F "tokens=*" %%I IN ("..\relative Path\*") DO echo absolute path: %%~fI
SvenS
15

Dies soll helfen, die Lücken in Adrien Plissons Antwort zu schließen (die bewertet werden sollte, sobald er sie bearbeitet ;-):

Sie können auch den vollständig qualifizierten Pfad Ihres ersten Arguments mithilfe von% ~ f1 abrufen. Dies gibt jedoch einen Pfad entsprechend dem aktuellen Pfad an, der offensichtlich nicht Ihren Wünschen entspricht.

Leider weiß ich nicht, wie ich die 2 zusammen mischen soll ...

Man kann damit umgehen %0und %1ebenso:

  • %~dpnx0für vollqualifiziertes Laufwerk + Pfad + Name + Erweiterung der Batchdatei selbst
    %~f0reicht ebenfalls aus;
  • %~dpnx1für vollqualifiziertes Laufwerk + Pfad + Name + Erweiterung des ersten Arguments [wenn das überhaupt ein Dateiname ist],
    %~f1reicht ebenfalls aus;

%~f1funktioniert unabhängig davon, wie Sie Ihr erstes Argument angegeben haben: mit relativen Pfaden oder mit absoluten Pfaden (wenn Sie die Dateierweiterung beim Benennen nicht angeben %1, wird sie nicht hinzugefügt, selbst wenn Sie sie verwenden %~dpnx1- jedoch.

Aber wie um alles in der Welt würden Sie eine Datei auf einem anderen Laufwerk überhaupt benennen, wenn Sie diese vollständigen Pfadinformationen überhaupt nicht in der Befehlszeile angeben würden?

Jedoch %~p0, %~n0, %~nx0und %~x0kann sich als nützlich erweisen, sollten Sie in Pfad interessiert sein (ohne Laufwerksbuchstaben), Dateiname (ohne Erweiterung), vollständigen Dateinamen mit der Erweiterung oder nur Dateinamenerweiterung. Beachten Sie jedoch, dass während %~p1und %~n1arbeiten Sie, um den Pfad oder Namen des ersten Arguments herauszufinden, %~nx1und %~x1fügen Sie die Erweiterung nicht + hinzu, es sei denn, Sie haben sie bereits in der Befehlszeile verwendet.

Kurt Pfeifle
quelle
Das Problem %~dpnx1besteht darin, dass der vollständig qualifizierte Pfad des Arguments relativ zum aktuellen Verzeichnis angegeben wird , während das OP den Pfad relativ zu dem Verzeichnis wünscht, in dem sich die Batchdatei befindet .
Adrien Plisson
@Adrien Plisson: %~dpnx1gibt den voll qualifizierten Pfad des 1. Arguments an. Nicht in Bezug auf alle, und nicht relativ zum aktuellen Verzeichnis entweder. Das dist für Laufwerk, das pist für Pfad, das nist für Dateiname ohne Suffix, das xist für Suffix, das 1ist für das erste Argument. - Und Nathans Frage war: "Gibt es eine Möglichkeit, einen absoluten Pfad von einem Wert zurückzugeben, der einen Dateinamen und / oder einen relativen Pfad enthält?"
Kurt Pfeifle
Sowohl absolute als auch relative Pfadverwendung und Quelle mit einer Beschreibung . Dankbar!
It3xl
10

Sie können hierfür auch Batch-Funktionen verwenden:

@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
Axel Heider
quelle
9

Kleine Verbesserung der hervorragenden Lösung von BrainSlugs83 . Verallgemeinert, um die Benennung der Ausgabeumgebungsvariablen im Aufruf zu ermöglichen.

@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

Wenn vom C:\projectAusgang ausgeführt wird:

C:\project\doc\build

quelle
4

Ich habe nicht viele Lösungen für dieses Problem gesehen. Einige Lösungen verwenden das Durchsuchen von Verzeichnissen mithilfe von CD, andere Batch-Funktionen. Meine persönliche Präferenz waren Batch-Funktionen und insbesondere die MakeAbsolute- Funktion von DosTips.

Die Funktion hat einige echte Vorteile, vor allem, dass sie Ihr aktuelles Arbeitsverzeichnis nicht ändert und zweitens, dass die ausgewerteten Pfade nicht einmal vorhanden sein müssen. Sie können ein paar hilfreiche Tipps , wie die Funktion nutzen hier auch.

Hier ist ein Beispielskript und seine Ausgaben:

@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

Und die Ausgabe:

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

Ich hoffe das hilft ... es hat mir sicher geholfen :) PS Nochmals vielen Dank an DosTips! Du rockst!

Dayneo
quelle
danke @ulidtko. Ich habe noch nie gegen Symlinks versucht.
Dayneo
2

Sie können sie einfach verketten.

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

es sieht seltsam aus mit \ .. \ in der Mitte Ihres Pfades, aber es funktioniert. Keine Notwendigkeit, etwas Verrücktes zu tun :)

Kristen
quelle
Das funktioniert einfach. Kann weiter vereinfacht werdenecho %~dp0..\SomeFile.txt
Cowlinator
1

In Ihrem Beispiel würde DIR / B / S .. \ somefile.txt aus Bar \ test.bat den vollständigen Pfad zurückgeben.

George Sisco
quelle
Das ist keine schlechte Idee ... sollte ich das Ergebnis des DIR mit einem FOR durchlaufen und einfach das erste Ergebnis holen?
Nathan Taylor
Ich habe über das Problem mit mehreren Dateien nachgedacht. Sie haben nicht angegeben, ob Vielfache ein Problem sind, also habe ich mich dafür entschieden. Was eine FOR-Schleife betrifft, habe ich Probleme, einer for-Schleife "../" zuzuführen, aber ymmv. Wenn Sie den Befehl DIR nicht ausführen können, können Sie die Ausgabe in eine Datei umleiten und diese durchlaufen. Ich sage nicht, dass die "Standard" -Methode zum Extrahieren des Pfades schlecht ist, sondern nur, dass ich dachte, meine hat mehr von dem getan, was Sie gefragt haben. Beide Lösungen haben etwas zu tun, und beide haben ihre eigenen Probleme.
George Sisco
Oh ... und wenn Sie DIR erfolgreich durchlaufen, können Sie die Umleitung in eine Datei überschreiben und das letzte Ergebnis erhalten. Wenn Sie die Reihenfolge in einer für Sie akzeptablen Weise umkehren, erhalten Sie nur das erste Ergebnis. Wenn Sie die Datei durchlaufen müssen, kann es auch hilfreich sein, die Reihenfolge umzukehren, um das erste Ergebnis zu erhalten, das sich an der letzten Position befindet. Der Grund, den ich vorschlage, ist, dass es möglicherweise einfacher ist, viele Dinge zu bekommen und zu verwerfen, als sie tatsächlich zu behandeln. Ich weiß nicht, ob meine Denkweise für Sie Sinn macht. Einfach als Idee rausbringen.
George Sisco
Wenn Sie einen Pfad zu einem Ordner normalisieren müssen, schlage ich diese Lösung vor: stackoverflow.com/questions/48764076/…
uceumern
0

PowerShell ist heutzutage ziemlich verbreitet, daher verwende ich es oft als schnelle Möglichkeit, C # aufzurufen, da dies Funktionen für so ziemlich alles bietet:

@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 ist ein bisschen langsam, aber die gewonnene Funktionalität ist schwer zu übertreffen, wenn nicht auf eine tatsächliche Skriptsprache zurückgegriffen wird.

stijn
quelle
0

Die Lösung von stijn funktioniert mit Unterordnern unter 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%
Irq Mishell
quelle
0

Dateien Alle anderen Antworten anzeigen

Verzeichnisse

Mit ..Ihrem relativen Pfad und der Annahme, dass Sie sich gerade in D:\Projects\EditorProject:

cd .. & cd & cd EditorProject (der relative Pfad)

gibt den absoluten Pfad zurück, z

D:\Projects

Ingenieur
quelle
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
Sergei Sachkov
quelle