Search code examples
windowsbatch-filecmd

Create folders named from part of filename before last underscore and move corresponding files


I use the following batch file for creating folders and moving corresponding files based on part before the underscore. However some files now include two underscores and Therefore I would like the script to produce folder names from the part before the last underscore, not the first.

Like this:

file1_e_cont.pdf --> file1_e\
file2_conter.pdf --> file2\

Any suggestions for tweaking this script to do this:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SRC=."
set "DST=."

for %%F in ("%SRC%\*_*.*") do if exist "%%F" (
    for /f "delims=_" %%D in ("%%~nF") do (
        if not exist "%DST%\%%D\" md "%DST%\%%D\"
        move "%SRC%\%%D_*.*" "%DST%\%%D\"
    )
)
endlocal & pause & exit /b

I'm aware there are several questions like mine around, however I haven't been able to implement these solutions on my case since this is all new to me.


Solution

  • This is a script based on the second approach from this answer:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    
    rem // Define constants here:
    set "_ROOT=%~dp0." & rem // (target directory; `.` means current, `%~dp0.` is parent of script)
    set "_MASK=*.pdf"
    
    setlocal EnableDelayedExpansion
    rem // Change into target directory:
    pushd "!_ROOT!" && (
        rem // Read input file line by line, ignoring empty lines:
        for /F delims^=^ eol^= %%L in ('dir /B /A:-D-H-S "!_MASK!"') do (
            rem // Store current base name:
            set "FILE=%%~nL"
            rem // Initialise interim variables:
            set "COLL=" & set "ITEM="
            rem /* Split base name at every `_` and loop through items
            rem    (`?`, `*`, `<`, `>` and `"` must not occur): */
            for %%I in ("!FILE:_=" "!") do (
                rem // Append previous item to variable:
                set "COLL=!COLL!_!ITEM!"
                rem // Store current item for next iteration, remove `""`:
                set "ITEM=%%~I"
            )
            rem // Create sub-directory named with appended string:
            if not exist "!COLL:~2!\*" mkdir "!COLL:~2!"
            rem // Move current file into sub-directory without overwriting:
            if not exist "!COLL:~2!\!FILE!%%~xL" (
                ECHO move "!FILE!%%~xL" "!COLL:~2!\!FILE!%%~xL"
            )
        )
        rem // Return from target directory:
        popd
    )
    endlocal
    
    endlocal
    exit /B
    

    Note that there must not occur any exclamation marks (!) in the paths!

    To actually move files, remove the upper-case ECHO command!
    To suppress 1 file(s) moved. messages, replace the upper-case ECHO command by > nul.