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
Related
I'm trying to set up a script that will install python automatically, and I'm stuck on setting up the user path. I have only a vague clue about what I'm doing here so please excuse me if I'm using any terms incorrectly.
I'm trying to set the environment variables using setx path "%PATH%;%LOCALAPPDATA%\Programs\Python\Python310"\ but I've run into several issues.
I finally have this command not failing because of 'multiple default arguments' or something but now when trying to set PATH, I get duplicate entries.
If originally %PATH% gave me '\path1;\path2', and I run setx path "%PATH%;\path3", %PATH% outputs '\path1;\path2;\path1;\path2;\path3',
when I expected to have '\path1;\path2;\path3'
As per what I've been reading from other answers, I think %PATH% gives you the combined SYSTEM and USER paths, but setx path modifies the USER path only. So everytime I run setx path I'm adding the system variables again.
I just want to add my python.exe location to the user path variable in a .bat script without this duplicating issue. Does anyone have a working solution?
#ECHO OFF
SETLOCAL
SET "python=%%USERPROFILE%%\AppData\Local\Microsoft\WindowsApps"
ECHO %path%>u:\pp.txt
FOR /f "tokens=1,2,*" %%u IN ('reg query HKCU\Environment') DO IF "%%u"=="Path" (
FIND /v "%python%" "u:\pp.txt" >NUL
IF NOT ERRORLEVEL 1 (
ECHO CHANGE path
ECHO SETX PATH "%%w;%python%"
)
)
DEL u:\pp.txt
GOTO :EOF
I used a path that I have installed as python. Note that the % need to be doubled.
Write the current path to a tempfile (u:\pp.txt is simply on a RAMDRIVE for me)
Read the environment data from the registry, tokenise and select for the first item in %%u being Path. Its value will be in %%w.
See whether the "python path" is already in the path; if not, errorlevel will be 1 so execute the setx.
I merely echoed the setx as I'm not going to change the registry. If the command echoed appears correct, remove the echo to actually execute the setx.
It may be an idea to also set the path in the current environment, as setx changes the variable's value for future instances, not for the current one.
===== Revision ==== in the light of comments:
#ECHO Off
SETLOCAL
SET "python=%%USERPROFILE%%\AppData\Local\Microsoft\WindowsApps"
ECHO %path%>u:\pp.txt
FOR /f "tokens=1,2,*" %%u IN ('reg query HKCU\Environment') DO IF /i "%%u"=="Path" (
FIND /v "%python%" "u:\pp.txt" >NUL
IF NOT ERRORLEVEL 1 (
SETLOCAL ENABLEDELAYEDEXPANSION
SET "xpath=%%w;%python%"
SET "xpath=!xpath:~159!"
IF DEFINED xpath (ECHO PATH too long) ELSE (
ECHO CHANGE path
ECHO SETX %%u "%%w;%python%"
)
ENDLOCAL
)
)
DEL u:\pp*.txt
GOTO :EOF
Fixes:
Comparison in for ...%%u made case-insensitive.
Length of resultant user-path variable checked. I used a value of 159 for testing, 1022 for real-world.
variable name being setx'd will be identical to that retrieved from the registry. (For me, it's Path (W11 22H2) - My editor helpfully changes any batch keyword followed by a space to upper-case)
I am writing a batch script that, quite reasonably, depends on "C:\WINDOWS\system32" being part of the PATH environment variable. I recently encountered a (developer's) machine that had a really weird path that didn't include system32, and therefore my batch script didn't work.
I looked up ways for my batch script to check the PATH variable and add system32 if it is not there. However, the solution I found used setx which ironically enough ALSO depends on system32 in the PATH variable. Are there any programmatic ways to add system32 to the PATH without it already being there?
Also please let me know if this is such an edge case that it doesn't make sense to make my script robust against it. I'm not expecting any of my typical users to have such a borked PATH variable. Should I bother?
try this:
for /f "delims=" %%a in ("%comspec%") do set "PATH=%PATH%;%%~DPa"
or this:
for /f "delims=" %%a in ("%comspec%") do set "compath=%%~DPa"
set "PATH=%PATH%;%compath:~0,-1%"
#ECHO OFF
SETLOCAL
SET "required=c:\windows\system32"
for %%p in ("%path:;=" "%") do (
FOR %%j IN ("" \) DO (
IF /i %%p=="%required%%%~j" GOTO :nextstep
)
)
SETx PATH "%required%;%path%"
:nextstep
ECHO PATH=%path%
GOTO :EOF
Here's my take on the problem. PATH may contain the required directory with or without a trailing \, and it may or may not have a preceding or trailing ;
I'd suggest that you examine the operation of SETX however. It sets an environment variable for FUTURE cmd sessions, not the CURRENT or EXISTING sessions, AFAIAA....and perhaps not for PATH or some other variables (I tried setting PATH using the SETX in the above batch - future sessions did NOT acquire the new value set, but it appeared to be set according to regedit32 - perhaps it needs a reboot - haven't investigated further at this stage)
Note that the above will need to have the required directory appended to %path% if my observations are borne out...
I my batch file i want to check if path to the following location is set
C:\Program Files(x86)\Windows Installer XML v3.5\bin
in any of the variable either in system variable or user defined variable
my need is to use content of the bin folder ; what if the user has not installed the way quoted above but has done something like
C:\WindowsinstallerXML\bin
or
D:\WindowsInstaller\bin
If you want to check every defined variable for the string, this would be the way. If you want to specifically check the path variable replacing set with echo %path% would work too.
set | find "C:\Program Files(x86)\Windows Installer XML v3.5\bin" > NUL 2>&1 || goto badinstall
If you want to check the install directory, I suggest checking to see if there is a registry value for it, if there is you can pull it like this.
for /f "tokens=3 delims= " %%a in ('reg query \HKCU\path\to\subkey\here /v "keyhere" ^| findstr /C:"keyhere"') do set wixmlpath=%%a
You can use:
PATH="C:\Program Files(x86)\Windows Installer XML v3.5\bin";%PATH%
Basically, instead of checking if it is set, why not to set explicitly in your script ?
#echo off
set input=%1
IF DEFINED input (ECHO you entered %input%) ELSE (ECHO usage: script bin_file_path)
If you know the name of one of the binary files, then you can use a FOR variable ~PATH: modifier to check if the path to that binary is currently in your PATH. And it will give you the actual path :)
for %F in (someFile.exe) do #if "%~$path:F" neq "" echo %~$path:F
If you want to use the code in a batch file, then each % must be doubled as %%.
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
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.