Search code examples
windowsbatch-filecmd

Move files batch doesn't work with double click but works from command line


I have some files

afjj.txt
agk.png
beta.tt
Ritj.rar

I use this script to move files in alphabetically order into autogenerated folders

a
|
|----> afjj.txt
|----> agk.png

b
|----> beta.tt

R
|----> Ritj.rar

To do I use this code

@echo off
setlocal

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

What is problem? If I double-click on this batch, batch doesn't work, not move.
But this batch works from command line.
Why?

NOTE: I use Windows Server 2016

P.S: from command line I use this command and works but not if I double click directly on .bat

move.bat "Z:\# 2020\Alfa\test"

Solution

  • The first mistake is naming the batch file move.bat. It is never a good idea to give a batch file the name of a Windows command because hat cause usually troubles. See also: SS64.com - A-Z index of Windows CMD commands.

    The second mistake is using setlocal enabledelayedexpansion inside the loop without a corresponding endlocal also within same loop executed as often as setlocal. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. The command SETLOCAL pushes several data on stack on every iteration of the loop and the data are never popped from stack in same loop on each loop iteration. The result is sooner or later a stack overflow depending on the number of files to process as more and more data are pushed on stack.

    The third mistake is the expectation that the current directory is always the directory of the batch file. This expectation is quite often not fulfilled.

    The fourth mistake is using a loop to iterate over a list of files which permanently changes on each execution of the commands in body of FOR loop. The loop in code in question works usually on storage media with NTFS as file system, but does not work on storage media using FAT32 or exFAT as file system.

    The fifth mistake is the expectation that %0 expands always to name of the currently executed batch file with file extension, but without file path which is not true if the batch file is executed with full qualified file name (drive + path + name + extension), or with just file name without file extension, or using a relative path.

    The sixth mistake is not enclosing the folder name on creation of the subfolder in double quotes which is problematic on file name starting unusually with an ampersand.

    The seventh mistake is not taking into account to handle correct file names starting with a dot like .htaccess in which case the second character must be used as name for the subfolder, except the second character is also a dot. It is very uncommon, but also possible that file name starts with one or more spaces. In this case also the first none space character of file name should be used as Windows by default prevents the creation of a folder of which name is just a space character.

    The solution is using following commented batch file with name MoveToFolders.cmd or MyMove.bat.

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    
    rem Get folder path of batch file assigned to an environment
    rem variable. This folder path ends always with a backslash.
    set "FilesFolder=%~dp0"
    
    rem Optionally support calling of this batch file with another folder
    rem path without checking if the passed string is really a folder path.
    if not "%~1" == "" set "FilesFolder=%~1"
    
    rem Replace all / by \ as very often people use / as directory separator
    rem which is wrong because the directory separator is \ on Windows.
    set "FilesFolder=%FilesFolder:/=\%"
    
    rem The folder path should end always with a backslash even on folder
    rem path is passed as an argument to the batch file on calling it.
    if not "%FilesFolder:~-1%" == "\" set "FilesFolder=%FilesFolder%\"
    
    rem Get a list of files in specified folder including hidden files loaded
    rem into the memory of running command process which does not change on
    rem the iterations of the loop below. Then iterate over the files list and
    rem move the files to a subfolder with first none dot and none space character
    rem of file name as folder name with the exception of the running batch file.
    for /F "eol=| delims=" %%i in ('dir "%FilesFolder%" /A-D /B 2^>nul') do if /I not "%FilesFolder%%%i" == "%~f0" (
        set "FileName=%%i"
        set "FirstPart="
        for /F "eol=| delims=. " %%j in ("%%i") do set "FirstPart=%%j"
        if defined FirstPart (
            setlocal EnableDelayedExpansion
            set "TargetFolderName=%FilesFolder%!FirstPart:~0,1!"
            md "!TargetFolderName!" 2>nul
            if exist "!TargetFolderName!\" move "%FilesFolder%!FileName!" "!TargetFolderName!\"
            endlocal
        )
    )
    
    rem Restore the previous execution environment.
    endlocal
    

    The batch file can be started also with a folder path as argument to process the files in this folder without checking if the passed argument string is really referencing an existing folder.

    Please read very carefully the answers on How to replace a string with a substring when there are parentheses in the string if there is interest on how to verify if a passed argument string really references an existing folder.

    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.

    • call /?
    • dir /?
    • echo /?
    • endlocal /?
    • for /?
    • if /?
    • md /?
    • move /?
    • rem /?
    • set /?
    • setlocal /?