2

I use this script to move folders in their own first letter directory

@echo off
setlocal enabledelayedexpansion

tree
for /d %%i in (*) do (
  set first=%%i
  set first=!first:~0,1!
  md !first! 2>nul
  if not "!first!" == "%%i" move "%%i" "!first!\%%i" 
)
tree

First letter directory are generated by this script. For example if I have folders name like these

Wachenfeldt - The
Пламень - Чужие
Von Stroheim - Lov

first letter directory that are generated by this script could be

W
П
V

I don't test this script if the first letter directory already exists before

Why is the problem? Script works partially because many folders are not moved in their own first letter directory and into CMD I read this error message:

file name or extension is too long

My result

Example of folders name that are moved

Wachenfeldt - The Interpreter (2019) Progressive Death Metal
Whitby Bay - Gothic Attack Vehicle (2018) Black Metal
Пламень - Чужие (2017) Black Metal (D5)

W
|
|----  Wachenfeldt - The Interpreter....
|----- Whitby Bay - Gothic Attack....

П
|
|----- Пламень - Чужие (20...

Example of folders name that are not moved (script doesn't works for them)

Withering Night - Within The Shadows We Lurk (2013) Black Metal (D5)
Woltemade - Siele Vir Die See (2019) Melodic Death Metal
Von Stroheim - Love_ Who Gets Love_ (2019) Doom_Post-Metal
aschipfl
  • 28,946
  • 10
  • 45
  • 77
Jack Rock
  • 137
  • 6

1 Answers1

2

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 in 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.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very 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.

Mofi
  • 38,783
  • 14
  • 62
  • 115