Loop over folder string and parse out last folder name - windows

I need to grab the folder name of a currently executing batch file. I have been trying to loop over the current directory using the following syntax (which is wrong at present):
set mydir = %~p0
for /F "delims=\" %i IN (%mydir%) DO #echo %i
Couple of issues in that I cannot seem to pass the 'mydir' variable value in as the search string. It only seems to work if I pass in commands; I have the syntax wrong and cannot work out why.
My thinking was to loop over the folder string with a '\' delimiter but this is causing problems too. If I set a variable on each loop then the last value set will be the current folder name. For example, given the following path:
C:\Folder1\Folder2\Folder3\Archive.bat
I would expect to parse out the value 'Folder3'.
I need to parse that value out as its name will be part of another folder I am going to create further down in the batch file.
Many thanks if anyone can help. I may be barking up the wrong tree completely so any other approaches would be greatly received also.

After struggling with some of these suggestions, I found an successfully used the following 1 liner (in windows 2008)
for %%a in (!FullPath!) do set LastFolder=%%~nxa

You were pretty close to it :) This should work:
#echo OFF
set mydir="%~p0"
SET mydir=%mydir:\=;%
for /F "tokens=* delims=;" %%i IN (%mydir%) DO call :LAST_FOLDER %%i
goto :EOF
:LAST_FOLDER
if "%1"=="" (
#echo %LAST%
goto :EOF
)
set LAST=%1
SHIFT
goto :LAST_FOLDER
For some reason the for command doesn't like '\' as a delimiter, so I converted all '\' to ';' first (SET mydir=%mydir:\=;%)

I found this old thread when I was looking to find the last segment of the current directory.
The previous writers answers lead me to the following:
FOR /D %%I IN ("%CD%") DO SET _LAST_SEGMENT_=%%~nxI
ECHO Last segment = "%_LAST_SEGMENT_%"
As previous have explained, don't forget to put quotes around any paths create with %_LAST_SEGMENT_% (just as I did with %CD% in my example).
Hope this helps someone...

This question's a little old, but I've looked for a solution more than once so here's a completely new take on it that I've just put together.
The trick is that we take the desired path, back up one level to create a folder mask for substitution and then replace the folder mask with nothing.
To test it, simple copy and paste into a command script (.cmd) in any directory, then run it. It will spit out only the deepest directory you're currently in.
Notes:
Replace %~dp0 with whatever path you like (as it is, it will return the deepest folder the batch file is run from. This is not the same as %cd%.)
When specifying the 'pathtofind' variable ensure there are no quotes e.g. c:\some path and not "c:\some path".
The original idea for folder masking is mine
Spaces in the path are no problem
Folder depth is not a problem
It was made possible by the genius of this batch scripting tip http://www.dostips.com/DtCodeBatchFiles.php#Batch.FindAndReplace
Hope this helps someone else.
#echo off
set pathtofind=%~dp0
if not exist %pathtofind% echo Path does not exist&pause>nul&goto :eof
cd /d %pathtofind%
set path1=%cd%
cd ..
set path2=%cd%
call set "path3=%%path1:%path2%\=%%"
echo %path3%
pause>nul

3 lines of script gets the result...
Found 2 additional ways to accomplish the goal, and unlike the other answers to this question, it requires no batch "functions", no delayed expansion, and also does not have the limitation that Tim Peel's answer has with directory deepness :
#echo off
SET CDIR=%~p0
SET CDIR=%CDIR:~1,-1%
SET CDIR=%CDIR:\=,%
SET CDIR=%CDIR: =#%
FOR %%a IN (%CDIR%) DO SET "CNAME=%%a"
ECHO Current directory path: %CDIR%
SET CNAME=%CNAME:#= %
ECHO Current directory name: %CNAME%
pause
REVISION: after my new revsion, here is an example output:
Current directory path: Documents#and#Settings,username,.sqldeveloper,tmp,my_folder,MY.again
Current directory name: MY.again
Press any key to continue . . .
This means that the script doesn't handle '#' or ',' in a folder name but can be adjusted to do so.
ADDENDUM: After asking someone in the dostips forum, found an even easier way to do it:
#echo off
SET "CDIR=%~dp0"
:: for loop requires removing trailing backslash from %~dp0 output
SET "CDIR=%CDIR:~0,-1%"
FOR %%i IN ("%CDIR%") DO SET "PARENTFOLDERNAME=%%~nxi"
ECHO Parent folder: %PARENTFOLDERNAME%
ECHO Full path: %~dp0
pause>nul

To return to the original poster's issue:
For example, given the following path:
C:\Folder1\Folder2\Folder3\Archive.bat
I would expect to parse out the value 'Folder3'.
The simple solution for that is:
for /D %%I in ("C:\Folder1\Folder2\Folder3\Archive.bat\..") do echo parentdir=%%~nxI
will give 'Folder3'. The file/path does not need to exist. Of course, .... for the parent's parent dir, or ...... for the one above that (and so on) work too.

Slight alteration for if any of the folders have spaces in their names - replace space to ':' before and after operation:
set mydir="%~p0"
set mydir=%mydir:\=;%
set mydir=%mydir: =:%
for /F "tokens=* delims=;" %%i IN (%mydir%) DO call :LAST_FOLDER %%i
goto :EOF
:LAST_FOLDER
if "%1"=="" (
set LAST=%LAST::= %
goto :EOF
)
set LAST=%1
SHIFT
goto :LAST_FOLDER

Sheesh guys, what a mess. This is pretty easy, and it's faster to do this in memory without CD.
This gets the last two directories of a path. Modify it as required to get the last tokens of any line. My original code I based this on has more complexity for my own purposes.
Fyi, this probably doesn't allow paths with exclamation marks since I'm using enabledelayedexpansion, but that could be fixed.
It also won't work on a plain drive root. This could be averted in a number of ways. Check what the input path ends with, or a counter, or modifying the token and check behaviour, etc.
#echo off&setlocal enableextensions,enabledelayedexpansion
call :l_truncpath "C:\Windows\temp"
----------
:l_truncpath
set "_pathtail=%~1"
:l_truncpathloop
for /f "delims=\ tokens=1*" %%x in ("!_pathtail!") do (
if "%%y"=="" (
set "_result=!_path!\!_pathtail!"
echo:!_result!
exit/b
)
set "_path=%%x"
set "_pathtail=%%y"
)
goto l_truncpathloop

I modified answer given by #Jonathan, since it did not work for me in a batch file, but this below does work, and also supports folders with spaces in it.:
for %%a in ("%CD%") do set LastFolder=%%~nxa
echo %LastFolder%
This takes the current directory and echoes the last, deepest folder, as in below example, if the folder is this:
C:\Users\SuperPDX\OneDrive\Desktop Environment\
The batch code echoes this: Desktop Environment

In batch files in the FOR command you'll need to prepend %whatever with an extra % (e.g. %%whatever).
'echo %~p0' will print the currently directory of the batch file.

This is what we had in the end (little bit more crude and can only go so deep :)
#echo off
for /f "tokens=1-10 delims=\" %%A in ('echo %~p0') do (
if NOT .%%A==. set new=%%A
if NOT .%%B==. set new=%%B
if NOT .%%C==. set new=%%C
if NOT .%%D==. set new=%%D
if NOT .%%E==. set new=%%E
if NOT .%%F==. set new=%%F
if NOT .%%G==. set new=%%G
if NOT .%%H==. set new=%%H
if NOT .%%I==. set new=%%I
if NOT .%%J==. set new=%%J
)
#echo %new%

I don't know if it's the version of windows I'm on (win2k3), but the FOR loop isn't giving me anything useful for trying to iterate through a single string.
According to my observation (and the FOR /? info) you get one iteration for each line of input to FOR, and there is no way to change this to iterate within a line. You can break into multiple tokens for a given line, but it is only one invocation of the FOR loop body.
I do think the CALL :LABEL approach in these answers does a great job. Something I didn't know until looking at this was that ";" and "," are both recognized as argument separators. So once you replace backslashes with semicolons, you can call your label and iterate through with SHIFT.
So working off of what is posted by others here, I have the below solution. Instead of grabbing the last folder name, I actually wanted to find everything up until some known directory name.. this is what is implemented below.
#echo off
if "%1"=="" goto :USAGE
set FULLPATH=%~f1
set STOPDIR=%2
set PATHROOT=
:: Replace backslashes with semicolons
set FULLPATH=%FULLPATH:\=;%
:: Iterate through path (the semicolons cause each dir name to be a new argument)
call :LOOP %FULLPATH%
goto :EOF
:LOOP
::Exit loop if reached the end of the path, or the stop dir
if "%1"=="" (goto :EOF)
if "%1"=="%STOPDIR%" (goto :EOF)
::If this is the first segment of the path, set value directly. Else append.
if not defined PATHROOT (set PATHROOT=%1) else (set PATHROOT=%PATHROOT%\%1)
::shift the arguments - the next path segment becomes %i
SHIFT
goto :LOOP
:USAGE
echo Usage:
echo %~0 ^<full path to parse^> ^<dir name to stop at^>
echo E.g. for a command:
echo %~0 c:\root1\child1\child2 child2
echo The value of c:\root1\child1 would be assigned to env variable PATHROOT

Unfortunatelly, this is working great only when put on some depth but have problems with being on the very top of the mountain... Putting this program into "C:\Windows" e.g. will result with... "C:\Windows", not expected "Windows". Still great job, and still damage can be repaired. My approach:
#echo off
set pathtofind=%~dp0
if not exist %pathtofind% echo Path does not exist&pause>nul&goto :eof
cd /d %pathtofind%
set path1=%cd%
cd ..
set path2=%cd%
set path4=%~dp1
call set "path3=%%path1:%path2%\=%%"
call set "path5=%%path3:%path4%*\=%%"
echo %path5%
pause>nul
And it's working just fine for me now, thanks for the idea, I was looking for something like that for some time.

Related

String replacement within FOR /F into batch file

There are a handful of questions on SO that look similar, but I cannot figure out some behaviour and I am looking for help.
Below is a snippet from a batch file I am trying to write which will load in a set of directories and potentially replace letter substitutions with an expanded path, e.g. the properties file might look like:
location1=C:\Test
location2=[m]\Test
Where location1 points to C:\Test and location2 points to C:\Program Files(x86)\MODULE\Test, because [m] is a shorthand to C:\Program Files(x86)\MODULE.
The batch script, to this point, is simply trying to read in the list of file paths and expand/replace the [m].
SET build.dir=%~dp0%
SET progfiles=%PROGRAMFILES(X86)%
IF "%progfiles%"=="" SET progfiles=%ProgramFiles%
SET local.properties=%build.dir%local.properties
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=1* delims==" %%i IN (%local.properties%) DO (
SET local.dir=%%j
SET local.dir=!local.dir:[m]=%progfiles%\MODULE!
echo !local.dir!
)
ENDLOCAL
Running this kicks out an error:
\MODULE was unexpected at this time.
If I replace the FOR with the following instead:
set test="[m]\Proj\Dir"
set test=!test:[m]=%progfiles%\MODULE!
echo %test%
I get the desired C:\Program Files(x86)\MODULE\Proj\Dir printed out...so I'm confused why it works fine outside of the FOR loop.
My understanding about delayed expansion is that it 'expands' at runtime...which you get to happen using !! instead of %% wrapped around the variable. Furthermore, as I'm creating the local.dir variable inside the FOR loop scope, I must use delayed expansion in order to access it with the updated value for the iteration.
I feel like the problem is using %progfiles%, like there's some special syntax I need to use in order to make it work but nothing is adding up for me. When I echo %progfiles%, it prints out as C:\Program Files(x86 -- note the missing trailing ).
Any ideas? Thanks
Tested suggestion:
D:\Projects\Test\Build>test
*** "D:\Projects\Test\Build\local.properties"
*** "","C:\Program Files (x86)"
[m]=C:\Program Files (x86)\MODULE
Adding quotes around the whole expression makes it work -- can't use other characters for some reason (like []) -- and since I want to append to the path later, we can safely remove the quotes afterwards:
SET local.dir="!local.dir:[m]=%progfiles%\MODULE!"
SET local.dir=!local.dir:"=!
Test this to see if you can nut out the issue:
The double quotes are to provide robust handling in a system with long file/path names.
The () are unquoted which are a problem in a batch script, when inside a loop.
#echo off
SET "build.dir=%~dp0%"
SET "progfiles=%PROGRAMFILES(X86)%"
IF "%progfiles%"=="" "SET progfiles=%ProgramFiles%"
SET "local.properties=%build.dir%local.properties"
echo *** "%local.properties%"
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "usebackq tokens=1* delims==" %%i IN ("%local.properties%") DO (
SET "local.dir=%%j"
echo *** "!local.dir!","%progfiles%"
SET "local.dir=!local.dir:[m]=%progfiles%\MODULE!"
echo !local.dir!
)
ENDLOCAL
pause
It has to do with the () characters that end up in your progfiles string. If you take them out, the substitution seems to work fine.
My suggestion is to ditch command for this particular purpose and use one of the other standard tools that Windows comes with. While my personal preference would be Powershell (since it's so much more powerful and expressive), you may just need something quick that you can integrate into existing cmd.exe stuff.
In that case, try the following VBScript file, xlat.vbs:
set arg = wscript.arguments
wscript.echo Replace(arg(0),arg(1),arg(2))
Your batch file then becomes something like, noting the inner for /f which captures the output of the VBS script and assigns it to the variable:
#echo off
SET build.dir=%~dp0%
set progfiles=%PROGRAMFILES(X86)%
if "%progfiles%"=="" set progfiles=%ProgramFiles%
set local.properties=%build.dir%local.properties
setlocal enabledelayedexpansion
for /f "tokens=1* delims==" %%i in (%local.properties%) do (
set local.dir=%%j
for /f "delims=" %%x in ('cscript.exe //nologo xlat.vbs "!local.dir!" "[m]" "%progfiles%\MODULE"') do set local.dir=%%x
echo !local.dir!
)
endlocal
Running that, I get the output:
C:\Test
C:\Program Files (x86)\MODULE\Test
which I think is what you were after.

Remove unwanted path name from %path% variable via batch

Scope: Windows XP or newer
Tools: Batch script
I need to be able to remove an unneeded path name from the system %PATH% variable. I know how to add a new path name to the system %PATH% variable, using a tool such as SETX.EXE, which also makes it immediately available within the existing CMD environment. It's probably a matter of using FIND and/or a FOR loop of some kind, but I'm not quite sure how to accomplish this. Here's a sample path statement...
%PATH% = C:\;C:\Program Files\Common Files\Java;C:\oracle\product\10.2.0\bin;C:\WINDOWS;C:\WINDOWS\system32;
From this, I need to be able to remove the full path name related to "oracle." So, in the above example, I need to be able to remove the "C:\oracle\product\10.2.0\bin" from the above path statement. Unfortunately, not only could the oracle path name be different than shown above, there could be multiple oracle path names and all need to be removed. I tried implementing the solution here...
How can I extract a full path from the PATH environment variable?
However, it just isn't working. The script wouldn't find the path name. Any help would be appreciated. Thank you.
This removes the substring C:\Program Files (x86)\Git\bin; from the PATH string and re-assigns:
set PATH=%PATH:C:\Program Files (x86)\Git\bin;=%
You might use this to see the change:
echo %PATH:C:\Program Files (x86)\Git\bin;=% | tr ; \n
Note: be exact on the substring. It's case-sensitive and slash-sensitive.
If you need to make it a persistent change use setx instead of set and open another console for changes to take effect.
setx /M PATH "%PATH:C:\Program Files (x86)\Git\bin;=%"
You can try something like this :
#echo off&cls
setlocal EnableDelayedExpansion
set $line=%path%
set $line=%$line: =#%
set $line=%$line:;= %
for %%a in (%$line%) do echo %%a | find /i "oracle" || set $newpath=!$newpath!;%%a
set $newpath=!$newpath:#= !
echo set path=!$newpath:~1!
I putted an echo to the last line. Check the result and If it's OK for you, remove it.
After trying SachaDee's answers I got errors with paths like
C:\Program Files (x86)
with brackets:
Program Files (x86)\Directory
gave me
Directorywas unexpected at this time. (no matter what time I tried it)
I added
set $line=%$line:)=^^)%
before the for-loop and
set $newpath=!$newpath:^^=!
after the loop (not sure if it is necessary)
#echo off
setlocal EnableDelayedExpansion
set path
set $line=%path%
set $line=%$line: =#%
set $line=%$line:;= %
set $line=%$line:)=^^)%
for %%a in (%$line%) do echo %%a | find /i "oracle" || set $newpath=!$newpath!;%%a
set $newpath=!$newpath:#= !
set $newpath=!$newpath:^^=!
set path=!$newpath:~1!
And it is now working.
I found the other solutions to this problem a bit awkward, I don't really want to rely on exact paths, complex 'delayed expansion' syntax, removing spaces for the 'for /f' loop and then adding them back in...
I think this is more elegant, and I commented the hell out of it so even someone new to the horrors of Batch can follow along.
::Turn off command display and allows environmental variables to be overridden for the current session
#echo off & setlocal
::Creates a unique file to use for the 'for loop'
set "TMPFILE="%temp%\tmp%RANDOM%%RANDOM%.txt""
::Duplicate PATH into OLDPATH
set "OLDPATH=%PATH%"
::Declare label for the 'goto' command
:Loop
::Extract the first text token with the default delimiter of semicolon
for /f "tokens=1 delims=;" %%G in ("%OLDPATH%") do (
REM Copy text token to TMPFILE unless what we want to remove is found
<NUL set /p="%%G" | find /i "StRiNgThAtMaTcHeSwHaTtOrEmOvE" >NUL 2>&1 || <NUL set /p="%%G;" >>%TMPFILE%
REM Remove text token from OLDPATH
set "OLDPATH=%OLDPATH:*;=%"
)
::Repeat loop until OLDPATH no longer has any delimiters, and then add any remaining value to TMPFILE
echo %OLDPATH% | findstr /C:";" >NUL && (goto :Loop) || <NUL set /p="%OLDPATH%" >>%TMPFILE%
::Set the path to TMPFILE
for /f "usebackq delims=" %%G in (%TMPFILE%) do (set "PATH=%%G")
::Clean-up
del %TMPFILE% >NUL 2>&1
::An echo and pause just for debug purposes
echo %PATH%
pause
I use this in CYGWIN to filter out CYGWIN paths before starting some Windows commands:
export PATH=`perl -e '#a=grep {$_ =~ /^\/cygdrive\//} split(":", $ENV{PATH});print join(":",#a)'`
I'm quite sure it's easy to adapt to Windows-native perl and bat files. Advantage: the flexible power of regular expressions.
I wanted to remove %LocalAppData%\Microsoft\WindowsApps; from PATH. But this was not possible due to using a another variable in the environment variable for Windows. The CALL hack is worked in SS64. (Also, thanks to Jens A. Koch for the base command.)
CALL set PATH=%PATH:%LocalAppData%\Microsoft\WindowsApps;=%
Of course, the PATH changing by SET will not be permanent. For fixed change, it is necessary to use the SETX command or directly change the entries in the Registry.
Actually, this solution was not needed to delete %LocalAppData%\Microsoft\WindowsApps; from PATH.
The %LocalAppData%\Microsoft\WindowsApps; is stored in the PATH entry of the Registry's HKCU\Environment key. Although it is more practical to delete this entry with the REG DELETE command, if there are another directories in the PATH entry, they will also be deleted, so new solution is needed.
I failed to remove the %USERPROFILE% variable syntax from SET (The %% symbol dilemma). Fortunately, PShell came to the rescue:
SET userprofile=
Powershell -c "$UserEnvironmentPath = [System.Environment]::GetEnvironmentVariable('Path', 'User'); $UserEnvironmentPath = $UserEnvironmentPath.Replace('%USERPROFILE%\AppData\Local\Microsoft\WindowsApps;',''); [Microsoft.Win32.Registry]::SetValue('HKEY_CURRENT_USER\Environment', 'Path', $UserEnvironmentPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)"
Special thanks to vonpryz for the last command. Because PowerShell's [System.Environment]::SetEnvironmentVariable command saves variables to Registry as REG_SZ even if their original value type is REG_EXPAND_SZ, which it's the known issue.
I wrote this code to simply remove any python executeable path from the path variable,
and insert my own specefic python version in the path so i can run python with
the versoin i wanted.
setlocal enableDelayedExpansion
set path`enter code here`
set $line=%path%
set $line=%$line: =#%
set $line=%$line:;= %
set $line=%$line:)=^^)%
set newpath=
for %%a in (%$line%) do (
echo %%a | find /i "python" ||set newpath=!newpath!;%%a
)
set path=!newpath!
set PATH=D:\python2.7\;%PATH%
#REM Rest of your script
python --version
#REM to exit the batch but not the window
exit /b
also, the first line is important! don't remove it or it wont work.
Notice: this code must run from a batch ".bat" file , if u want to copy paste this code in cmd window, you must replace all "%%a" to "%a" in this code.
If you know a file that exists within the directory you want to remove (e.g. want to remove all paths that might include java.exe), the following will work very straightforwardly by simply doing string replacement, no need to parse the path, etc:
#REM Executable to look for
set CMD=java.exe
:search
#REM Find the executable anywhere in the path
for %%a in (%CMD%) do set FOUND=%%~$PATH:a
if "%FOUND%"=="" goto done
#REM Strip \cmd.ext so we just have the directory
set FOUND=!FOUND:\%CMD%=!
#echo Found %CMD% in %FOUND%
#echo Removing %FOUND% from path...
set "PATH=!PATH:%FOUND%=!"
#REM Clean up any lone leftover \ in the path (in case the path was C:\foo\ instead of C:\foo)
set PATH=%PATH:;\;=;%
goto search
:done

batch script variable unset in for loop has no effect

Below is my script. I am trying to look into folders one level below and pick out only those folders, hence the ~-9 which extracts the last 9 chars from the path. But the set var= does not unset the variable because the output comes back with the same folder name repeated # times. Also batch doesn't allow me to do this extract trick directly on %%i, hence the need for the local variable.
How do I clear this variable so that it takes the new value in the next iteration?
#echo off
for /d %%i in (%1\*) do (
set var=%%i
echo %var:~-9%
set "var="
)
http://judago.webs.com/variablecatches.htm has an explanation for my problem. The magic lines were setlocal enabledelayedexpansion and calling var as echo !var:~-9!. ! vs % ...wow! cmd still amazes me.
You found the source of your problem, as well as the solution - delayed expansion.
But using FOR while delayed expansion is enabled can cause problems if any of the filenames contain the ! character. The expansion of the for variable %%i will be corrupted if the value contains ! and delayed expansion is enabled. This is not a frequent problem, but it happens.
The solution is to toggle delayed expansion on and off within the loop
#echo off
setlocal disableDelayedExpansion
for /d %%i in (%1\*) do (
set var=%%i
setlocal enableDelayedExpansion
echo !var:~-9!
endlocal
)
I'm also wondering what you mean by "I am trying to look into folders one level below and pick out only those folders, hence the ~-9 which extracts the last 9 chars from the path". I suspect your are trying to get the name of the child folder, without the leading path information. If that is so, then using the substring operation is not a good solution because the length of folder names varies.
There is a very simple method to get the name of the folder without the leading path info:
for /d %%i in (%1\*) do echo %%~nxi

How would you write a .bat or .cmd file to remove an element from the PATH?

Related:
How to list the elements of the path in a batch file?
How does FOR work?
How would you write a batch file or CMD file to remove an element from the path? It should handle gracefully:
differences in case
shortnames and long names
I've done this using tr.exe but it's slow and complicated and uses temporary files, which makes it even more complicated.
I think the answer is something like this:
setlocal
set tpath=""
set _path="%PATH:;=" "%"
for %%p in (%_path%) do (
call :KeepIfNotEqual %%p %elementToRemove%
)
endlocal & set path=%tpath%
...where %elementToRemove% is the path element to remove. KeepIfUnique would have to be a subroutine that takes two arguments - directory names, normalizes them, and appends the first argument to tpath if it is not equal to the 2nd argument (elementToRemove).
As I said, I can do this with tr.exe, but can I do it with just built-in commands in the windows cmd.exe shell?
EDIT: I guess when you get right down to it, the question is, how to do case-conversion in cmd.exe?
This is what I came up with, using Igor's hint.
#echo off
goto START
-------------------------------------------------------
rmpath.bat
remove a path element from path
Created Tue Sep 15 21:33:54 2009
-------------------------------------------------------
:START
SETLOCAL ENABLEDELAYEDEXPANSION
#REM require one argument (the path element to remove)
if _%1==_ goto USAGE
#REM ~fs = remove quotes, full path, short names
set fqElement=%~fs1
#REM convert path to a list of quote-delimited strings, separated by spaces
set fpath="%PATH:;=" "%"
#REM iterate through those path elements
for %%p in (%fpath%) do (
#REM ~fs = remove quotes, full path, short names
set p2=%%~fsp
#REM is this element NOT the one we want to remove?
if /i NOT "!p2!"=="%fqElement%" (
if _!tpath!==_ (set tpath=%%~p) else (set tpath=!tpath!;%%~p)
)
)
set path=!tpath!
#call :LISTPATH
goto ALL_DONE
-------------------------------------------------------
--------------------------------------------
:LISTPATH
echo.
set _path="%PATH:;=" "%"
for %%p in (%_path%) do if not "%%~p"=="" echo %%~p
echo.
goto :EOF
--------------------------------------------
--------------------------------------------
:USAGE
echo usage: rmpath ^<arg^>
echo removes a path element from the path.
goto ALL_DONE
--------------------------------------------
:ALL_DONE
ENDLOCAL & set path=%tpath%
if command has ignore case option
if /I "Blah"=="blah" (echo true) else (echo false)
See if /? for more help on using it. I generally found these commands helpful in determining how to do some cmd line trickery:
if /?
call /?
for /?
set /?
I want to add something to Cheeso's answer... but I don't have enough rep to add a comment.
If you want the script he provided to work even after the batch file is finished, then change the last line like this this:
#echo off
. . .
. . .
SETLOCAL ENABLEDELAYEDEXPANSION
. . .
. . .
endlocal & set path=%tpath%
This employs a neat trick: the 'tpath' variable is available when that line is read-in and parsed by cmd.exe and expanded prior to getting deleted by 'endlocal'.
This allows the changes to the 'PATH' variable to persist even after the batch file exits.

Resolve absolute path from relative path and/or file name

Is there a way in a Windows batch script to return an absolute path from a value containing a filename and/or relative path?
Given:
"..\"
"..\somefile.txt"
I need the absolute path relative to the batch file.
Example:
"somefile.txt" is located in "C:\Foo\"
"test.bat" is located in "C:\Foo\Bar".
User opens a command window in "C:\Foo" and calls Bar\test.bat ..\somefile.txt
In the batch file "C:\Foo\somefile.txt" would be derived from %1
In batch files, as in standard C programs, argument 0 contains the path to the currently executing script. You can use %~dp0 to get only the path portion of the 0th argument (which is the current script) - this path is always a fully qualified path.
You can also get the fully qualified path of your first argument by using %~f1, but this gives a path according to the current working directory, which is obviously not what you want.
Personally, I often use the %~dp0%~1 idiom in my batch file, which interpret the first argument relative to the path of the executing batch. It does have a shortcoming though: it miserably fails if the first argument is fully-qualified.
If you need to support both relative and absolute paths, you can make use of Frédéric Ménez's solution: temporarily change the current working directory.
Here's an example that'll demonstrate each of these techniques:
#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
If you save this as c:\temp\example.bat and the run it from c:\Users\Public as
c:\Users\Public>\temp\example.bat ..\windows
...you'll observe the following 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"
the documentation for the set of modifiers allowed on a batch argument can be found here:
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/call
I came across a similar need this morning: how to convert a relative path into an absolute path inside a Windows command script.
The following did the trick:
#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%
Most of these answers seem crazy over complicated and super buggy, here's mine -- it works on any environment variable, no %CD% or PUSHD/POPD, or for /f nonsense -- just plain old batch functions. -- The directory & file don't even have to exist.
CALL :NORMALIZEPATH "..\..\..\foo\bar.txt"
SET BLAH=%RETVAL%
ECHO "%BLAH%"
:: ========== FUNCTIONS ==========
EXIT /B
:NORMALIZEPATH
SET RETVAL=%~f1
EXIT /B
Without having to have another batch file to pass arguments to (and use the argument operators), you can use FOR /F:
FOR /F %%i IN ("..\relativePath") DO echo absolute path: %%~fi
where the i in %%~fi is the variable defined at /F %%i. eg. if you changed that to /F %%a then the last part would be %%~fa.
To do the same thing right at the command prompt (and not in a batch file) replace %% with %...
This is to help fill in the gaps in Adrien Plisson's answer (which should be upvoted as soon as he edits it ;-):
you can also get the fully qualified path of your first argument by using %~f1, but this gives a path according to the current path, which is obviously not what you want.
unfortunately, i don't know how to mix the 2 together...
One can handle %0 and %1 likewise:
%~dpnx0 for fully qualified drive+path+name+extension of the batchfile itself,
%~f0 also suffices;
%~dpnx1 for fully qualified drive+path+name+extension of its first argument [if that's a filename at all],
%~f1 also suffices;
%~f1 will work independent of how you did specify your first argument: with relative paths or with absolute paths (if you don't specify the file's extension when naming %1, it will not be added, even if you use %~dpnx1 -- however.
But how on earth would you name a file on a different drive anyway if you wouldn't give that full path info on the commandline in the first place?
However, %~p0, %~n0, %~nx0 and %~x0 may come in handy, should you be interested in path (without driveletter), filename (without extension), full filename with extension or filename's extension only. But note, while %~p1 and %~n1 will work to find out the path or name of the first argument, %~nx1 and %~x1 will not add+show the extension, unless you used it on the commandline already.
You can also use batch functions for this:
#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
Small improvement to BrainSlugs83's excellent solution. Generalized to allow naming the output environment variable in the call.
#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
If run from C:\project output is:
C:\project\doc\build
I have not seen many solutions to this problem. Some solutions make use of directory traversal using CD and others make use of batch functions. My personal preference has been for batch functions and in particular, the MakeAbsolute function as provided by DosTips.
The function has some real benefits, primarily that it does not change your current working directory and secondly that the paths being evaluated don't even have to exist. You can find some helpful tips on how to use the function here too.
Here is an example script and its outputs:
#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
And the 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
I hope this helps... It sure helped me :)
P.S. Thanks again to DosTips! You rock!
You can just concatenate them.
SET ABS_PATH=%~dp0
SET REL_PATH=..\SomeFile.txt
SET COMBINED_PATH=%ABS_PATH%%REL_PATH%
it looks odd with \..\ in the middle of your path but it works. No need to do anything crazy :)
In your example, from Bar\test.bat, DIR /B /S ..\somefile.txt would return the full path.
PowerShell is pretty common these days so I use it often as a quick way to invoke C# since that has functions for pretty much everything:
#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%
It's a bit slow, but the functionality gained is hard to beat unless without resorting to an actual scripting language.
stijn's solution works with subfolders under 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%
Files See all other answers
Directories
With .. being your relative path, and assuming you are currently in D:\Projects\EditorProject:
cd .. & cd & cd EditorProject (the relative path)
returns absolute path e.g.
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

Resources