@ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: First, create a list of subdirectorynames; iterate using `%%a`
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" '
) DO (
REM clear initial flags for each dir
SET "flag1="
SET "flag2="
REM Now read the subdirectory. set flag1 for first file, flag2 for second or later
FOR /f %%g IN (
'dir /b /a-d "%sourcedir%\%%a\*" 2^>nul'
) DO IF DEFINED flag1 (SET flag2=y) ELSE (SET flag1=y)
REM if neither flag set, directory is empty
REM if both are set, dir has 2 or more files
REM so - if flag1 but not flag2 is set then exactly 1 file.
IF DEFINED flag1 IF NOT DEFINED flag2 ECHO(MOVE "%sourcedir%\%%a\*" "%sourcedir%\"
REM if flag2 is not set, subdirectory is now empty
IF NOT DEFINED flag2 ECHO(rd "%sourcedir%\%%a"
)
GOTO :EOF
You would need to change the setting of sourcedir
to suit your circumstances.
The required MOVE commands are merely ECHO
ed 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
)
The required RD commands are merely ECHO
ed for testing purposes. After you've verified that the commands are correct, change ECHO(RD
to RD
to actually create the directories.
I'd suggest you target the batch on a tree that contains three subdirectories containing 0,1 and 2 files for testing.
Yur batch fails for a number of reasons.
- The metavariable (loop-control variable) in a batch file requires a double
%
in every reference
- environment variables are referenced by
%var%
, not %var
.
- within a block statement (a parenthesised series of statements),
%var%
will refer to the value of var
at the time the block is initially encountered, not as the variable changes within the loop. This is the delayed expansion
problem - well-documented here.
Note that this batch does not check whether a candidate to be moved already exists in the destination directory. If it does, then the move
and rd
statements will generate an error report and the file and directory will remain as-is.
NB: do not change any rem
line to the ::
form as ::
is in fact a broken label which terminates a block (ie. will cause problems)
Edit - revision
@ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: First, create a list of subdirectorynames; iterate using `%%a`
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" '
) DO (
REM clear initial flags for each dir
ECHO %%a
SET "subdir=%%a"
SET "flag1="
SET "flag2="
REM Now read the subdirectory. set flag1 for first file, flag2 for second or later
FOR /f "delims=" %%g IN (
'dir /b "%sourcedir%\%%a\*" 2^>nul'
) DO SET "name=%%g"&IF DEFINED flag1 (SET flag2=y) ELSE (SET flag1=y)
REM if neither flag set, directory is empty
REM if both are set, dir has 2 or more files/dirs
REM so - if flag1 but not flag2 is set then exactly 1 file/dir
IF DEFINED flag1 IF NOT DEFINED flag2 CALL :moveit
REM if flag2 is not set, subdirectory is now empty
IF NOT DEFINED flag2 rd "%sourcedir%\%%a"
)
GOTO :EOF
:moveit
REM NAME may be a file or a directory - "%sourcedir%\%%a\%NAME%\.." exists if directory
IF NOT EXIST "%sourcedir%\%subdir%\%NAME%\.." MOVE "%sourcedir%\%subdir%\*" "%sourcedir%\"
IF EXIST "%sourcedir%\%subdir%\%NAME%\.." IF NOT EXIST "%sourcedir%\%name%" MOVE "%sourcedir%\%subdir%\%NAME%" "%sourcedir%\"
GOTO :EOF
Caution! The above batch does NOT use ECHO( hence it will attempt to
move or delete files or directories. Use only on test subtree!
This minor revision moves single-directories as well as single-files up within the directories on the level below the target.
It displays sufficient information to infer the source of error messages if it can't move or delete as directed.
Essentially, it's the same as the original, exceot that a subroutine is called to make the changes. The subroutine uses name
for the directoryname it hopes to move and subdir
for the subdirectory being processed.