I'm using Win-10 64-bit.
Given the following directory structure on my windows machine...
c:\tmp\Logs_UAT\
'-> folder1
| '-> test.txt
'-> folder2 '-> test.txt...I'm trying to rename all files in each directory by prefixing them with the directory name, to have:
c:\tmp\Logs_UAT\
'-> folder1
| '-> folder1_test.txt
'-> folder2 '-> folder2_test.txtIn order to achieve this, I'm looping recursively through files in the %basedir% using FOR and in each directory I'm getting a current directory name using the inner FOR "loop", and then doing a rename. However, current directory is usually not derived correctly. Here's the .bat file which allows to reproduce the issue:
@echo off
set basedir=c:\tmp\Logs_UAT
for /R %basedir% %%I in (*) DO (
cd %%~pI
for %%G in (.) do echo %%~nxG> temp.temp
set /P curdir=<temp.temp
del /f temp.temp
echo.
echo -------------------
echo %%I
dir
echo %curdir%
echo -------------------
)It outputs the same value for %curdir% for both files:
C:\tmp>test.bat
-------------------
c:\tmp\Logs_UAT\folder1\test.txt Datenträger in Laufwerk C: ist OSDisk Volumeseriennummer: D280-2DC0 Verzeichnis von C:\tmp\Logs_UAT\folder1
06.05.2020 19:29 <DIR> .
06.05.2020 19:29 <DIR> ..
06.05.2020 19:02 0 test.txt 1 Datei(en), 0 Bytes 2 Verzeichnis(se), 291.684.151.296 Bytes frei
folder2
-------------------
.
-------------------
c:\tmp\Logs_UAT\folder2\test.txt Datenträger in Laufwerk C: ist OSDisk Volumeseriennummer: D280-2DC0 Verzeichnis von C:\tmp\Logs_UAT\folder2
06.05.2020 19:29 <DIR> .
06.05.2020 19:29 <DIR> ..
06.05.2020 19:02 0 test.txt 1 Datei(en), 0 Bytes 2 Verzeichnis(se), 291.684.151.296 Bytes frei
folder2
-------------------
C:\tmp\Logs_UAT\folder2>I tried:
- Fresh console window
- Console and batch file
- Sometimes I'm getting the first folder name and sometimes second
- Lots of search on the internet
I'd prefer to stick to standard windows utilities as I have to distribute the script to other users which might not have the extra software.
How do I get the current directory name in a windows command prompt inside of FOR loop?
23 Answers
Indeed DelayedExpansion helped, thanks DavidPostill. Here's the working script:
@echo off
SETLOCAL EnableDelayedExpansion
set basedir=c:\tmp\Logs_UAT
for /R %basedir% %%I in (*) DO (
cd %%~pI
for %%G in (.) do echo %%~nxG> temp.temp
set /P curdir=<temp.temp
del /f temp.temp
echo !curdir!
)
endlocal - For you to get the same result as in your question, in one loop:
@echo off
set "_=echo\ -------------------"
for /D /R "c:\tmp\Logs_UAT" %%I in (*
)do %_% & echo\%%~nxI & dir "%%~nxI" & echo\ %%~nxI & %_%- Your output:
------------------- Volume in drive C has no label. Volume Serial Number is 32E8-70C5 Directory of c:\tmp\Logs_UAT\folder1
05/06/2020 02:40 PM <DIR> .
05/06/2020 02:40 PM <DIR> ..
05/06/2020 02:41 PM 8 test.txt 1 File(s) 8 bytes 2 Dir(s) 3,128,999,936 bytes free folder1 ------------------- ------------------- Volume in drive C has no label. Volume Serial Number is 32E8-70C5 Directory of c:\tmp\Logs_UAT\folder2
05/06/2020 02:47 PM <DIR> .
05/06/2020 02:47 PM <DIR> ..
05/06/2020 02:48 PM 7 folder2.txt 1 File(s) 7 bytes 2 Dir(s) 3,128,999,936 bytes free folder2 -------------------You don't need to use more than one
forloop, usefor /D /R, in one loop recursive in all subdirectories, and this makesDelayedExpansionnot necessaryFOR /R - Loop through files (recursively) FOR /D - Loop through several folders/directoriesRem :: Set your folder /Directory /Recursively tree starting at "c:\tmp\Logs_UAT"For /D /R "c:\tmp\Logs_UAT"
Why need to create and delete a file
temp.tempin the entire directory usingfor /Dwith any additionalforloop? When you already have the name of the current folder in your loop (/Recursive in all sub/Directories) stored in the variable%%~nxI(and many others available expansively too), so, for to use in your looping, just use them...Use the FOR variable syntax replacement: %~pI - expands %I to a path only %~nI - expands %I to a file name only %~xI - expands %I to a file extension onlyThe modifiers can be combined to get compound results: %~pnI - expands %I to a path and file name only %~pnxI - expands %I to a path, file name and extension only)do %_% & echo\%%~nxI & dir "%%~nxI" & echo\ %%~nxI & %_%Obs.: About using
%%~xindirectoryname observation note inss64.com:Full Stop BugAlthough Win32 will not recognise any file or directory name that begins or ends with a '.' (period/full stop) it is possible to include a Full Stop in the middle of a directory name and this can cause issues with FOR /D.Parameter expansion will treat a Full Stop as a file extension, so for a directory name like "Sample 2.6.4" the output of %%~nI will be truncated to "Sample 2.6" to return the whole folder name use %%I or %%~nxI
You don't need to use
DelayedExpansion, you can usecmd /v/c echo\!curdir!orecho\%%~nxIThat's exactly what your code is doing in your question (and answer), you are using multiple
forloops, where theDelayedExpansionwill not be necessary if you used a singlefor /D /Rwhen using%%~nxIFor the same result and use on a selected line, if you are using one or more
forloops, you can also enableDelayedExpansionat run time usingcmd /v /c echo\!curdir!expanding the variable value in that time.)do set "curdir=%%~nxI" && cmd /v /c " echo\!curdir!"
For get output as the same of your code output question, you can set/use a variable for your banner (
set _=echo\ ----), adjust this to desired layout output:)do %_% & echo\%%~nxI & dir "%%~nxI" & echo\ %%~nxI & %_%
Or, in one conventional formatting
@echo off set "_=echo\ -------------------" for /D /R "c:\tmp\Logs_UAT" %%I in (*)do ( %_% echo\%%~nxI dir "%%~nxI" echo\ %%~nxI %_% )
For one simple and short output option in one line with
for /D /Rloop, you can try:@echo off for /d /r "c:\tmp\Logs_UAT" %%I in (*)do set "curdir=%%~nxI" && cmd /v /c " echo\!curdir!"
Some further reading:
[√] Set
[√] CMD /?
[√] For Loop
[√] For /D Loop
[√] For /R Loop
[√] DelayedExpansion
My general solution to find all levels of directories:
@ECHO OFF
setlocal enabledelayedexpansion EnableExtensions
::Sample BAT to find all levels directories
:: FULL current path: "G:\First our directory\Second our directory\Third. - Strange . directory\Fourth and last directory"
:: Get full current path (could be passed as a parameter to use as subroutine)
SET ActualP=%CD%
:: Set starting counter number
SET /A Countdir=0
:: Find number of directorie levels, echoes them to console and set associated variables
FOR %%G in ("%ActualP:\=" "%") DO (SET /A "CountDir+=1" set "Lvl=%%G" set "Lvl!Countdir!=!Lvl!" ECHO Lvl!Countdir!:!Lvl! )
ECHO.
:: Check number of levels to console
ECHO Levels of directorie: %CountDir%
ECHO.
:: Get last (current) level of directory free of double quotes and echoes it to console
:: between brackets to assure not extra chars are present
SET "LastLvl=%Lvl:"=%"
ECHO LastLvl: [%LastLvl%]
:: Just to be sure that the variables have been set:
ECHO.
ECHO Third level of directorie: %Lvl3%
PAUSE
EXIT
:: This wiil echoes to console:
::
:: Lvl1:"G:"
:: Lvl2:"First our directory"
:: Lvl3:"Second our directory"
:: Lvl4:"Third. - Strange . directory"
:: Lvl5:"Fourth and last directory"
::
:: Levels of directorie: 5
::
:: LastLvl: [Fourth and last directory]
::
:: Third level of directorie: "Second our directory"
:: Premere un tasto per continuare . . .