Windows batch file copying/renaming files - windows

I've been working on creating a batch file which moves files from one dir to another dir and if the filename already exists rename it then move it over.
I'm really new to creating batch files so heres what I have so far
set temp=C:\Users\Daniel\Desktop\a\a1
set dir=C:\Users\Daniel\Desktop\a\
set /a "counter=0"
set "duplicate=-copy^("
set "bracket=^)"
if exist "%temp%" ( ^
for %%i in (%temp%\*) ^
do ^
if exist "%dir%\%%~ni%%~xi" ( call :checkFileName %%~ni %%~xi) ^
ELSE ( move %temp%\%%~ni%%~xi %dir% ) )^
ELSE ( echo doesnt exist)
:checkFileName
echo test
set fileName=%1
set fileExtenstion=%2
set /a "counter+=1
rem Do whatever you want here over the files of this subdir, for example:
if exist %dir%%fileName%%duplicate%%counter%%bracket%%fileExtenstion% ( IF defined %1 (
IF defined %2 (call :checkFileName %1 %2 )) ) ELSE (ren %temp%\%fileName%%fileExtenstion% %fileName%%duplicate%%counter%%bracket%%fileExtenstion% )
timeout 30
goto :eof
:increment
set /a "counter+=1"
goto :eof
I've no idea to increment a var before calling my checkFileName function. I think recursively calling the same function is the right idea but I'm a bit rusty with the commands/syntax as I only started this on friday.
Any advice or pointers would be appreciated. (If you know any useful links/books that are worth a look let me know!)
timeout 600

#ECHO OFF
SETLOCAL
set "tempdir=C:\Users\Daniel\Desktop\a\a1"
set "dir=C:\Users\Daniel\Desktop\a"
set "tempdir=U:\sourcedir\t w o"
set "dir=U:\destdir"
set "duplicate=-copy("
set "bracket=)"
if exist "%tempdir%" (
for %%i in ("%tempdir%\*") do (
if exist "%dir%\%%~nxi" ( call :checkFileName "%%~ni" "%%~xi"
) ELSE (
move "%tempdir%\%%~nxi" "%dir%" >nul
)
)
) ELSE (
echo doesnt EXIST
)
GOTO :eof
:checkFileName
set "fileName=%~1"
set "fileExtenstion=%~2"
set /a counter=0
:nexttry
set /a counter+=1
rem Do whatever you want here over the files of this subdir, for example:
if exist "%dir%\%fileName%%duplicate%%counter%%bracket%%fileExtenstion%" GOTO nexttry
move "%tempdir%\%fileName%%fileExtenstion%" "%dir%\%fileName%%duplicate%%counter%%bracket%%fileExtenstion%" >nul
goto :eof
Here's a revised version. I'll explain the changes I've made:
#echo off turns off command-echoing
setlocal ensures any changes made to the environment are backed-out when the procedure ends.
I've added to extra sets to re-set the directories to suit my system. You'd need to delete these two lines for yours.
temp is a special name which points to a temporary directory. One of quite a few. Best not to use that particular name - replaced with tempdir
set when used for a numeric set doesn't require quotes. In a string-set, the syntax set "var=value" is used to ensure that trailing spaces on the command-line are not included into the value assigned (which can cause chaos - spaces are sort of - invisible.) Note that in a string set, spaces on both sides of the = are significant...
I prefer to assign directorynames into variables without the trailing \. This allows the value to be extended with the least gymnastics. Personal preference - but you used it both ways...
The carets are not required before ( and are only required before ) where the syntax would close an open parenthesis (ie. in a parenthesised statement-sequence as may occur in an if, else or do.) Used arbitrarily, this can lead to stray literal carets in filenames, for instance.
carets at end-of-line is a valid but easily-lost and a little-used technique. The rule for breaking statements over multiple lines is crudely, keep do, if or else on the same physical line as its ( and else on the same physical line as the closing-parenthesis that precedes it. Then no eol-caret is required.
Batch simply charges on through statements. It has no concept of the end of a procedure and needs to be told when the procedure ends. This can be done with a goto :eof statement (which jumps to the physical end-of-file) or an exit /b statement (which returns from a subroutine, optionally setting errorlevel. goto :eof effectively does the smae thing in most circumstances and is way more common.)
%%~nxi means the name-and-extension of the file %%i. Of course, it's quite legal to use %%~ni and its counterpart individually, but it's not necessary. Note however that these parts should be despatched in "quotes" to the subroutine because each part may contain spaces. "quotes"make a spaces-containing-string appear as one string with spaces rather than a series of strings.
>nul redirects the move command's report "1 file(s) moved" to the bit-bucket.
Setting the two variables within checkfilename should be done after removing the quotes applied in the call - that's the purpose of the ~ before the parameter-number.
counter can be set to zero, then incremented.
If the proposed new filename exists, then simply increment the number and try again until you hit a name that doesn't exist. Yes - counter will run out eventually. It tops out at 2**31-1. Might take a while...
Note the use of quotes in the if exist and move. This is to guard against spaces in file/directorynames. The same goes for the for %%i in ("%tempdir%\*") used earlier...you may notice that in my testing, I used (deliberately) a directoryname that contained spaces. As it happens, the filenames I used also had spaces in them.
One last warning - There is no doubt that some odd filenames may choke on these procedures, but they should be few and far between. Filenames containing carets may be a problem, for instance.
Welcome to batch!

Unless this is a learning project, I recommend you study the XCOPY command.

Related

Batch - Inner For-Loop token parameter does not accept delayed Expanded variable within Outer For Loop [duplicate]

This question already has answers here:
Issue with *For loop* and *delayed expansion*
(2 answers)
Closed 4 months ago.
I have numerous mp4 files inside a folder with can take the form below
Dancing_2022-10-30_00-05-32_015_Paris.mp4
Eating_in_the_Restaurant_2022-07-03_04-21-25_497_London.mp4
My goal is to create two folders and move the respective files
I want the last underscore (_) delimited item (in this case Paris and London) extracted to create the first folders and then extract the date part (2022-10-30 and 2022-07-03) and create the subfolder. Then move the files accordingly.
My biggest challenge is to extract the tokens. Usually does allows tokens to be extracted from left to right but this assumes the file names are consistent. However in my case the files names are not consistent. There is some consistency from right to left with regards to the placement of the _ delimited parts I want to extract.
I have a draft batch script but the issue I have is that CMD does not allow delayed expanded variable to be passed to the inner For-Loop within the Outer For loop. Any clues to the solution. Thanks
This is my draft batch script.
#echo off
Setlocal EnableDelayedExpansion
for %%F in ("*.mp4") do (
set file=%%~nF
echo !file!
set files="!file:_=" "!"
echo file=!files!
set cnt=-4
for %%a in (!files!) do set /a cnt+=1
echo !cnt!
for /F "tokens=!cnt!* delims=_" %%a in ("!file!") do set output=%%b
echo Output=%output%
for /F "tokens=1-4 delims=_" %%a in ("!output!") do echo %%d
)
endlocal
pause
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
FOR %%e IN ("%sourcedir%\*.mp4") DO (
SET "fname=%%~ne"
SET "fname=!fname:_=,!"
SET "fname=!fname: =#!"
FOR %%y IN (!fname!) DO SET "fdate=!ftime!"&SET "ftime=!fmsec!"&SET "fmsec=!flocation!"&SET "flocation=%%y"
ECHO MD "!flocation:#= !\!fdate!"
ECHO MOVE "%%e" "!flocation:#= !\!fdate!\"
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Relatively easy to follow. Foe each filename, take the name part. replace each _ with , and each Space with #
The result in fname would thus be a comma-separated list of elements, so process each one, cascading them through the variables.
Finally, replace the # with Space when creating the subdirectory and filename.
Note that the switch between Space and # is intended to take care of places like New York and Los Angeles
== Supplemental == in response to comment:
FOR %%e IN ("%sourcedir%\*.mp4") DO (
SET "fname=%%~ne"
SET "fname=!fname:_=,!"
SET "fname=!fname: =#!"
FOR %%y IN (!fname!) DO SET "fdate=!ftime!"&SET "ftime=!fmsec!"&SET "fmsec=!flocation!"&SET "flocation=%%y"
SET "fname=!fname:,=_!"
CALL SET "fname=%%fname:_!fdate!_!Ftime!_!fmsec!_!flocation!=%%
ECHO MD "!flocation:#= !\!fname!\!fdate!"
ECHO MOVE "%%e" "!flocation:#= !\!fname!\!fdate!\"
ECHO MD "!flocation:#= !\!fname:_= !\!fdate!"
ECHO MOVE "%%e" "!flocation:#= !\!fname:_= !\!fdate!\"
)
After locating the required parts, replace each , with _ in fname, then replace the part from the date onwards with nothing, yielding the activity in fname.
Two sets of reports generated now - one for activity with underscores, the second with spaces.

Passing variable out of setlocal code

When writing scripts in Windows batch files, sometimes the proper execution of the script requires the use of the setlocal command. My main complaint about using setlocal is that I'm often performing complicated for & if statements in which I set variable values in that section of code. These settings are lost when I issue the command endlocal.
So far I've worked around this by echoing the variable value into a scratch file within the setlocal segment and then read the value back into the variable after the endlocal. However, it seems like there should be a more elegant solution to the problem.
The suggested answer provides a handy way to circumvent the issue if only using one or multiple set statements. However, the linked answer does not provide a solution when the setlocal is in place to allow a for loop to properly expand variable names at time of execution rather than parsing. In my situation, I also have if statement logic tree to perform further checking on that information to set many different possible variable values. The linked solution does not provide a solution to this situation.
This code is supposed to check an install package for its version number. It is called from another script that requires that version number to function correctly. We know the package is named using the form [application][version number].msi. The [version number] can be any of the following:
7
7.5
9
9.5
10
10.1
It is possible that more than one install package exists in the designated directory so it's important to look through them all and select the highest version in the directory.
I inherited and expanded the code to correctly process the 10 & 10.1 versions. If there's a better way to do this without the setlocal (e.g. I've thought of rewriting to use a case statement) I'd love to see a better solution.
However, I'm still interested in learning how to pass variables out of the setlocal / endlocal segment.
setlocal enabledelayedexpansion
for /f %%a in ('dir /b program*.MSI') do (
set FileName=%%a
set tst1=!FileName:~4,3!
if "!tst1!" == "msi" (
rem Only true if it has a 1 digit number (e.g. "9")
set MAIN_VERSION=!FileName:~2,1!
) else (
set tst2=!FileName:~5,3!
if "!tst2!" == "msi" (
rem Only true if it has a 2 digit version number (e.g. "10")
set MAIN_VERSION=!FileName:~2,2!
) else (
... lots more code ...
)
)
)
rem Write results out to a file for temporary storage. This particular
rem form of echo is required to ensure there are no trailing spaces, CR,
rem or LF
echo|set /P ="!MAIN_VERSION!" > %USERPROFILE%\UGV.txt
rem End local variables
endlocal
rem Read the correct version out of our temporary storage file
set /p MAIN_VERSION=<%USERPROFILE%\UGV.txt
How do I pass a variable (such as MAIN_VERSION above) out of a Windows batch script setlocal / endlocal code segment without using a scratch file?
To preserve variables over the setlocal/endlocal scope, there exists different solutions.
It depends of the situation which one you should use.
1) Simple
Works only outside of parenthesis blocks with simple content, but can produce problems with special characters like !^".
setlocal
set localVar=something simple
...
(
endlocal
set "out=%localVar%"
)
set out
2) Medium
Works also in blocks and can handle the most characters, but can fail with !^ and linefeed/carriage return
if 1==1 (
setlocal EnableDelayedExpansion
set "localVar=something medium nasty & "^&"
for /F "delims=" %%V in ("!localVar!") DO (
endlocal
set "out=%%V"
)
)
set out
3) Advanced
Works for all contents in any situation
SO: preserving exclamation marks in variable between setlocals batch

Brackets aren't working on this Windows File?

I am new here and my english is not very good so first, pardon me.
My problem is that this batch keep reading the strings inside the variable "IF EXIST %FSIZE%"; i meant, in case that variable does not exist, the batch keep reading inside the () brackets instead go on the rest of the stings.
If %FSIZE% exist, the batch perform the 2 task i assign: (1.) If size is equal, goto cifiles5x. (2.) If size is NOT equal it uses 7z to extract the file i want to be there.
If %FSIZE% DOES NOT EXIST, the batch keep saying "765952 was not expect at this moment".
I am taking the advices on ss64.com like don't use brackets or quotes when comparing numeric values (%size% EQU 765952) but i don't understand why it does not continue to where the ) ends.
I have also try to link the commands with "&&" so i can erase the brackets but the results are the same.
I know there's 2 spaced patches without quotes; they are unquoted because if i did the size checker won't work.
Thanks for reading this.
EDIT: Batch modified according to suggestions made.
#ECHO OFF
TITLE Log checker
COLOR 0F
SET FSIZE=%ProgramFiles(x86)%\Ces\Log Files Ver\LogVbReg_r2.dll
SET InsDIR=%ProgramFiles(x86)%\Ces\Log Files Ver\
REM I didn't add "" on FSIZE and InsDIR because if i did, quote the variable will
REM result a doubled quoted patch and won't work.
CLS
ECHO ==============================================================================
ECHO = Log checker =
ECHO ==============================================================================
ECHO Checking if exist:
ECHO "%FSIZE%"
IF EXIST "%FSIZE%" (
ECHO It does exist, checking size...
FOR %%A IN ("%FSIZE%") DO SET SIZE=%%~ZA
IF "%SIZE%" EQU "765952"
ECHO Size is right, jumping to CIFILES5
GOTO CIFILES5x
) ELSE (
ECHO Size is not right, extracting the needed file...
7z.exe e "Data_X.*" -o"%InsDIR%" -y "LogVbReg_r2.dll"
GOTO CIFILES5x)
ECHO Does not exist; extracting file...
REN Data_X.* Data_X.exe
Data_X.exe
TIMEOUT 2>NUL
REN DATA_X.* Data_X.dat
:CIFILES5x
ECHO Reach cifiles5x
PAUSE
IF EXIST "%TEMP%\9513.CES" (GOTO OPS) ELSE (GOTO LNKD)
You have space in your file name of %FSIZE%. As result text after space it is treated as command for if. Try using quotes around "%FSIZE%" or do CD to folder first and than check for just file name.
You have several minor errors:
If the file name may have spaces, it MUST be enclosed in quotes in all cases:
IF EXIST "%FSIZE%" (
FOR /F is used to read file CONTENTS. If you want to process file NAME, don't use /F option, but plain FOR command:
FOR %%A IN ("%FSIZE%") DO SET SIZE=%%~ZA
The ELSE part of an IF command must appear in a line alone:
GOTO CIFILES5x
) ELSE (
The same thing for a parentheses:
GOTO CIFILES5x
)
Excepting if the opening parentheses appear in the same line. This is correct:
IF EXIST "%TEMP%\9513.CES" (GOTO OPS) ELSE (GOTO LNKD)
If you compare numbers for equality (EQU, NEQ) then don't matter if they are enclosed in quotes or not. Just in case of GTR, GEQ, LSS and LEQ they must have NOT quotes. However, in your case, this is not a problem...

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

Loop over folder string and parse out last folder name

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.

Resources