1

For example I have a folder called:

FIRSTNAME LASTNAME

And I have the files:

FIRSTNAME LASTNAME CHK#123
FIRSTNAME LASTNAME CHK#145

I want to make a batch to move all files automatically. I have this code from another post:

@echo off
setlocal enabledelayedexpansion
pushd "C:\Folders\"
for %%a in (*) do (
    set fldr=%%~na
    set fldr=!fldr:~0,4!
    md "!fldr!"
    move "%%a" "!fldr!"
)
popd
pause
exit

But it creates a folder with the first four characters if the file has more than four characters.

What I want to do is that the batch file evaluates the file name, stops at separator string  CHK# and moves the file to the folder based on first and last name in file name left to  CHK#.

Mofi
  • 46,139
  • 17
  • 80
  • 143
Gergő
  • 13
  • 2
  • would you consider using a powershell script rather than a batch file? – CodeNeedsCoffee Apr 01 '19 at 19:16
  • Do the folders already exist, or do you have to create them (by the script)? – aschipfl Apr 01 '19 at 19:18
  • Powershell is ok too :) And yes, they usually exist but not all the time. – Gergő Apr 01 '19 at 19:20
  • 1
    Given your example, `!fldr:~0,4!`, would be, `FIRS`! – Compo Apr 01 '19 at 19:31
  • If the folders exist in advance, I'd iterate though them and move matching files within: `for /D %%I in (*) do move "%%~nI*.*" "%%~I"`. If they do not exist, it becomes a bit more complicated, because you have to split the file names based on certain rules, which you need to define clearly (first name and last name do not contain spaces on their own, both are not empty, no middle name, etc.)... – aschipfl Apr 01 '19 at 19:40

3 Answers3

1

A PowerShell solution using a RegEx to get the file name parts in front of CHK# by using
a lookahead zerolength assertion

## Q:\Test\2019\04\01\SO_55462032.ps1

Pushd "C:\Folder"

Get-ChildItem *CHK#* -File | Where Name -match '^(.*)(?= CHK#)' | ForEach-Object {
   If(!(Test-Path $Matches[1])){New-Item -ItemType Directory -Name $Matches[1]}
   $_ | Move-Item -Destination $Matches[1]
}
PopD

Sample output:

> tree /F
├───FirstName
│       FirstName CHK#654
│
├───FIRSTNAME LASTNAME
│       FIRSTNAME LASTNAME CHK#123
│       FIRSTNAME LASTNAME CHK#145
│
├───Firstname Middlename Lastname
│       Firstname Middlename Lastname CHK#987
│
└───Title Firstname MiddleName Lastname
        Title Firsname MiddleName Lastname CHK#159
  • Save with any name and `.ps1` extension, To run it in an open powershell window either supply a path,or if in current dir precede the name with `.\` other than cmd it won't execute from current dir without that. –  Apr 01 '19 at 20:54
  • Thank you mate! I really appreciate your help! – Gergő Apr 01 '19 at 21:04
  • Just saw that comment formatting mangled the text. I meant prepend the file name with dot backslash if in current folder. –  Apr 01 '19 at 21:06
  • If answers solve your question or you find them helpful you should consider to [accept the answer](http://stackoverflow.com/help/accepted-answer) and/or [vote up](https://stackoverflow.com/help/why-vote) –  Apr 01 '19 at 21:08
1

Here is a simple batch file solution for this task:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
for %%I in ("C:\Folders\* CHK#*") do call :ProcessFile "%%I"
endlocal
goto :EOF

:ProcessFile
set "FileName=%~nx1"
set "CheckSum=%FileName:* CHK#= CHK#%"
call set "FolderName=%%FileName:%CheckSum%=%%"
md "%~dp1%FolderName%" 2>nul
move /Y %1 "%~dp1%FolderName%"
goto :EOF

The string after  CHK# must not have an equal sign or one or more percent signs.

The subroutine ProcessFile assigns with a string substitution the string part beginning from first occurrence of CHK# to end of file name to environment variable CheckSum.

One more string substitution is used to remove the checksum string from file name to get the folder name. This command line is double parsed by Windows command processor because of command call to replace on first parsing %CheckSum% by current value of environment variable CheckSum and replace both %% by just a single %. On second parsing the remaining set "FolderName=%FileName: CHK#123=%" is processed resulting in assigning to environment variable FolderName the string FIRSTNAME LASTNAME for the first file name example. See also How does the Windows Command Interpreter (CMD.EXE) parse scripts?

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.

Mofi
  • 46,139
  • 17
  • 80
  • 143
0

One way to split the file name into parts is to use the space and # as delims
and check if the 3rd token is CHK

:: Q:\Test\2019\04\01\SO_55462032.cmd
@echo off  
setlocal enabledelayedexpansion 
pushd "C:\Folders\" 
for %%F in ("* * CHK#*") do for /f "tokens=1-4 delims=# " %%A in ("%%F") Do (   
    if "%%C"=="CHK" (
        if not Exist "%%A %%B" MD "%%A %%B"
        Move "%%F" "%%A %%B"
    ) else if "%%D"=="CHK" (
        if not Exist "%%A %%B %%C" MD "%%A %%B %%C"
        Move "%%F" "%%A %%B %%C"
    )

) 
popd 
pause 
exit /b

Sample output:

> SO_55462032.cmd
        1 Datei(en) verschoben.
Drücken Sie eine beliebige Taste . . .
> tree a:\ /F
├───FIRSTNAME LASTNAME
│       FIRSTNAME LASTNAME CHK#123
│       FIRSTNAME LASTNAME CHK#145
│
└───Firstname Middlename Lastname
        Firstname Middlename Lastname CHK#987
  • And what if there is a middle name? – Gergő Apr 01 '19 at 19:40
  • 2
    Then your specification fell short, and I'll add an else and another if. –  Apr 01 '19 at 19:42
  • Ah its working, you are my hero! How can i edit it to be able to deal with names with only firstnames, and names longer than 3 part? – Gergő Apr 01 '19 at 19:59
  • Well I kept the solution as simple as possible to match your **INITIAL** specs, while it is possible to add some more IFs and else I think a solution in PowerShell featuring full fledged Regular Expressions is more adequate, wait a minute for another answer. –  Apr 01 '19 at 20:07