It turns out that for … do
is “greedy”.
(I don’t remember knowing this before,
and it doesn’t seem to be prominently documented in for /?
.)
Here’s what I mean:
Consider script cow1.bat
:
@echo off
for %%a in (how now brown) do echo %%a
echo cow
Pretty trivial.
When run, it outputs
how
now
brown
cow
as we might guess.
Consider script cow2.bat
:
@echo off
for %%a in (how now brown) do echo %%a & echo cow
The same, except lines 2 and 3 have been joined, delimited by a &
.
Guess what!?
When run, it outputs
how
cow
now
cow
brown
cow
It’s being interpreted as if it were
@echo off
for %%a in (how now brown) do (echo %%a & echo cow)
(Changing &
to &&
didn’t have any effect.)
In a (to me) bigger surprise,
@echo off
for %%a in (how now brown) do (echo %%a) & echo cow
also functioned the same as cow2
.
I had to use
@echo off
(for %%a in (how now brown) do echo %%a) & echo cow
to get the original, cow1
behavior.
How does this affect the OP?
for %%F in (*) do move "%%F" "..\%%~nxD - %%F" >nul && popd && rd /s /q "%%D"
is analogous to cow2
:
If %%D
(the directory we just pushd
’ed into) is beatles
,
then the above code segment does this:
move "george" "..\beatles - george" >nul && popd && rd /s /q "beatles"
move "john" "..\beatles - john" >nul && popd && rd /s /q "beatles"
move "paul" "..\beatles - paul" >nul && popd && rd /s /q "beatles"
move "ringo" "..\beatles - ringo" >nul && popd && rd /s /q "beatles"
(whitespace added for clarity).
So it’s moving the first file (george
),
and then popd
’ing out of the beatles
directory and deleting it,
leaving the other files to be reaped by the roomba.
Recommendation (TL;DR)
Try
for /f "eol=: delims=" %%D in ('dir /b /s /ad *^|sort /r') do (pushd "%%D" && (for %%F in (*) do move "%%F" "..\%%~nxD - %%F" >nul) && popd && rd /s /q "%%D")
↑ ↑
Trivial question:
Why do we need the *
in the dir /b /s /ad *
subcommand?
&&
means only run the command if the previous was successful. There might be moves that are failing in the inner loop? You can use just&
to string the commands together.DO
portion of the FOR loop aren't happy with being separated by&&
. Also, the opening echo off and final exit statements are probably unnecessary.