I suggest the following batch code for this folder moving task:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
%SystemRoot%\System32\tree.com
for /F "eol=| delims=" %%I in ('dir /AD /B 2^>nul') do (
set "FolderName=%%I"
setlocal EnableDelayedExpansion
set "TargetFolder=!FolderName:~0,1!"
if not "!TargetFolder!" == "!FolderName!" (
md "!TargetFolder!" 2>nul
move /-Y "!FolderName!" "!TargetFolder!\"
)
endlocal
)
%SystemRoot%\System32\tree.com
endlocal
Most important for this task on moving folders into subfolders is to get first loaded into memory the entire list of folder names before processing each folder name with the FOR loop. On using just for /d %%i in (*) do
the command FOR iterates over a list of folder names get name by name directly from the file system and this list changes with each execution of the commands inside of the loop because of creating perhaps an additional folder and moving a folder into a subfolder. That can easily result in one folder processed multiple times (no problem here) or is skipped because of a change of folders in current folder while FOR iterates over the list. Especially on FAT32 and exFAT drives on which the directory entries are not sorted local specific in alphabetic order it is really essential to get first the complete list of folders loaded into memory and then start iterating over the folders in the list in memory which is not affected by the modifications in file system during execution of the commands in the loop.
FOR with option /F
results here in starting in background one more command process with %ComSpec% /c
and the command line within '
appended as additional arguments. So executed is in background with Windows installed into C:\Windows
:
C:\Windows\System32\cmd.exe /c dir /AD /B 2>nul
The command DIR executed by the background command process searches in current directory for
- just folders because of option
/AD
(attribute directory) including folders with hidden attribute set ignored by for /D
and
- just outputs the folder names without path in bare format because of option
/B
.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul
. The redirection operator >
must be escaped with caret character ^
on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir
command line with using a separate command process started in background.
The output written to handle STDOUT of the started command processes is captured by the command process which is processing the batch file and is interpreted by FOR line by line after the started command process terminated itself after finishing execution of command DIR.
FOR with option /F
ignores by default empty lines which is no problem here. A line is split up into substrings using by default normal space and horizontal tab as string delimiters. That line splitting behavior is disabled with delims=
which defines an empty list of delimiters because of folder names can contain one or more spaces. A line is also ignored if the first substring (= entire line in this case) is starting with default end of line character ;
which is not wanted here as a folder name can start with a semicolon. eol=|
redefines the end of line character to a vertical bar which no folder name can contain ever like ?
or *
which could be used too.
Delayed expansion is not enabled on assigning the current folder name to an environment variable because otherwise a folder name containing one or more !
would not be correct processed by the batch file. The Windows command processor would interpret exclamation marks in folder name as beginning/end of a delayed expanded environment variable reference on the command line set "FolderName=%%I"
if delayed environment variable expansion would be enabled already on processing this command line.
See also: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
But it is necessary to enable delayed expansion for the further commands as described for example by Variables are not behaving as expected and so the command setlocal EnableDelayedExpansion
is used to enable delayed expansion now.
Important is to reference in the following command lines always the environment variable FolderName
with delayed expansion and don't use %%I
to process also correct folder names with one or more exclamation marks.
The currently processed folder is moved into a subfolder of which name is the first character of current folder name if the current folder name is not already a single character folder name and of course the creation of the target folder is successful at all and the current folder can be really moved because of no running process uses this folder or one of its subfolders as current folder and no running process has any file in this folder or its subfolders opened with preventing the deletion respectively movement of the file as long as being opened by the process and the target folder does not contain already a folder with same name.
Then the previous execution environment must be restored before processing the next folder name from list in memory. Read this answer for details about the commands SETLOCAL and ENDLOCAL as there is more done than just enabling and disabling delayed expansion for each folder name in the list.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
dir /?
echo /?
endlocal /?
for /?
if /?
md /?
move /?
set /?
setlocal /?
tree /?
Note: This batch file solution does not work for folders containing Unicode characters. A PowerShell script like this one written by mklement0 should be used if folders contain non ASCII characters.