Home > front end >  Renaming and moving files while in a loop in a batch script
Renaming and moving files while in a loop in a batch script

Time:03-06

So I want to be able to drag one or more rar or zip files to a batch .cmd script that extracts the file with 7zip to a directory one level up, renames the extracted file to the current folder name and then deletes the folder with the rar or zip archives.

So far I have:

set work=%temp%\%random%%random%%random%%random%
mkdir "%work%" || goto :eof

for %%A in (*.zip *.rar) do (
    "%ProgramFiles%\7-Zip\7z.exe" e -o"%work%" "%%~A"
    for %%F in ("%work%\*") do move "%%~F" "%%~nA%%~xF"
)

rmdir "%work%"

And that extracts the file to the current directory and renames it to the archive name but I want to name it to the directory the archive is in so %%~nA needs to be changed but typing "for /?" doesn't seem to give me an option to get the current directory. Then I need to move the extracted file one level up and delete the folder with the archive files, I just don't know how to reference the files when using move and rmdir when in a loop.

This is my first attempt at batch scripting please go easy on me.

CodePudding user response:

This is going to be more of a lesson on how to do reasonably good programing in *cough* a bad *cough* the batch programming language.

The first stage is to work from the outside inwards, or top down. It only took a few minutes to build this framework. The sub goals/steps are:

  1. Simply loop through all of the parameters and see if we can save them to a variable and then echo them out.
  2. Figure out how to extract the SubFolderName (This will later be the file name), and echo it per each loop.
  3. Build the Extract folder and echo per each loop.
  4. Realize you forgot you were going to need the folder path of the calling batch file, add it as first parameter, and extract it before entering the :ltpLoop.

The %1 gets the first parameter, while SHIFT will shift all parameters so that %1=%2, %2=%3, %3=%4, %4=%5, etc... This effectively removes the first parameter and reduces the total number of parameters by 1. The IF statement exits the script when SHIFT has removed all parameters and %1 no longer has a value.

Also, a lot of people will follow the ECHO command with a period (ECHO.Text to echo), but I had one bad case where period printed a blank line instead of the text, but semicolon ; was fine - so I no do period, period!

@ECHO OFF
    GOTO :Start

:LoopThroughParameters
    SET CmdPath=%~1
    SHIFT
    ECHO;[[[%CmdPath%]]]
:ltpLoop
    IF [%1] EQU [] GOTO :EOF
    SET Folder=%~1
    SET SubFolderName=%~nx1
    SET ExtractFolder=%~1\Extract

    ECHO;
    ECHO;[%Folder%]
    ECHO;[%SubFolderName%]
    ECHO;[%ExtractFolder%]
    SHIFT
    GOTO :ltpLoop

:Start
    CALL :LoopThroughParameters "%~dp0" %*
PAUSE

In stage 2 we start by cleaning up the code by removing the unneeded echos.

:LoopThroughParameters
    SET CmdPath=%~1
    SHIFT

:ltpLoop
    IF [%1] EQU [] GOTO :EOF
    SET Folder=%~1
    SET SubFolderName=%~nx1
    SET ExtractFolder=%~1\Extract
    <<New Code Will Go Here>>
    SHIFT
    GOTO :ltpLoop

Start experimenting in the <<New Code Will Go Here>> area.

    ECHO;FOR %%C IN ("%Folder%\*.zip" "%Folder%\*.rar") DO (

The code is tested by creating 5 folders, with one zip file in each folder, and dragging the folders on to the batch file and seeing if we like the commands it built.

FOR %C IN ("D:\Temp\StackOverflow\71345433\volume5\*.zip" "D:\Temp\StackOverflow\71345433\volume5\*.rar") DO (
FOR %C IN ("D:\Temp\StackOverflow\71345433\volume1\*.zip" "D:\Temp\StackOverflow\71345433\volume1\*.rar") DO (
FOR %C IN ("D:\Temp\StackOverflow\71345433\volume2\*.zip" "D:\Temp\StackOverflow\71345433\volume2\*.rar") DO (
FOR %C IN ("D:\Temp\StackOverflow\71345433\volume3\*.zip" "D:\Temp\StackOverflow\71345433\volume3\*.rar") DO (
FOR %C IN ("D:\Temp\StackOverflow\71345433\volume4\*.zip" "D:\Temp\StackOverflow\71345433\volume4\*.rar") DO (

Then we try to build the extraction commands:

    FOR %%C IN ("%Folder%\*.zip" "%Folder%\*.rar") DO (
        ECHO;"%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C"
    )

Check if results look correct:

"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume5\Extract" "D:\Temp\StackOverflow\71345433\volume5\Last Zip file.zip"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume1\Extract" "D:\Temp\StackOverflow\71345433\volume1\SomeZipFile.zip"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume2\Extract" "D:\Temp\StackOverflow\71345433\volume2\JustAnotherZip.zip"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume3\Extract" "D:\Temp\StackOverflow\71345433\volume3\YetAnotherZip.zip"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume4\Extract" "D:\Temp\StackOverflow\71345433\volume4\How Many Zips Was This.zip"

In stage 3 we try it for real - remove the echo:

"%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C"

Check if we like the results (You can see I'm using some old latitude-e5440 PDFs I happen to find in a forgotten folder someplace):

D:\Temp\StackOverflow\71345433>dir /s /b
D:\Temp\StackOverflow\71345433\DragDropAndExtract.CMD
D:\Temp\StackOverflow\71345433\volume1
D:\Temp\StackOverflow\71345433\volume2
D:\Temp\StackOverflow\71345433\volume3
D:\Temp\StackOverflow\71345433\volume4
D:\Temp\StackOverflow\71345433\volume5
D:\Temp\StackOverflow\71345433\volume1\Extract
D:\Temp\StackOverflow\71345433\volume1\SomeZipFile.zip
D:\Temp\StackOverflow\71345433\volume1\Extract\lat_e_reimage_guide_en-us.pdf
D:\Temp\StackOverflow\71345433\volume2\Extract
D:\Temp\StackOverflow\71345433\volume2\JustAnotherZip.zip
D:\Temp\StackOverflow\71345433\volume2\Extract\latitude-e5440-laptop_owners-manual_en-us.pdf
D:\Temp\StackOverflow\71345433\volume3\Extract
D:\Temp\StackOverflow\71345433\volume3\YetAnotherZip.zip
D:\Temp\StackOverflow\71345433\volume3\Extract\latitude-e5440-laptop_user's guide_en-us.pdf
D:\Temp\StackOverflow\71345433\volume4\Extract
D:\Temp\StackOverflow\71345433\volume4\How Many Zips Was This.zip
D:\Temp\StackOverflow\71345433\volume4\Extract\latitude-e5440-laptop_users-guide_en-us.pdf
D:\Temp\StackOverflow\71345433\volume5\Extract
D:\Temp\StackOverflow\71345433\volume5\Last Zip file.zip
D:\Temp\StackOverflow\71345433\volume5\Extract\latitude-e5440-laptop_white papers_en-us.pdf

For stage 4 we echo both the extract command and xcopy command to see what they look like:

        ECHO;"%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C"
        ECHO; XCOPY /V /C /Y "%ExtractFolder%\*.*" "%CmdPath%\%SubFolderName%.*"

Just a reminder, every test is done by dragging the 5 folders onto the batch file:

"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume5\Extract" "D:\Temp\StackOverflow\71345433\volume5\Last Zip file.zip"
XCOPY /V /C /Y "D:\Temp\StackOverflow\71345433\volume5\Extract\*.*" "D:\Temp\StackOverflow\71345433\\volume5.*"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume1\Extract" "D:\Temp\StackOverflow\71345433\volume1\SomeZipFile.zip"
XCOPY /V /C /Y "D:\Temp\StackOverflow\71345433\volume1\Extract\*.*" "D:\Temp\StackOverflow\71345433\\volume1.*"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume2\Extract" "D:\Temp\StackOverflow\71345433\volume2\JustAnotherZip.zip"
XCOPY /V /C /Y "D:\Temp\StackOverflow\71345433\volume2\Extract\*.*" "D:\Temp\StackOverflow\71345433\\volume2.*"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume3\Extract" "D:\Temp\StackOverflow\71345433\volume3\YetAnotherZip.zip"
XCOPY /V /C /Y "D:\Temp\StackOverflow\71345433\volume3\Extract\*.*" "D:\Temp\StackOverflow\71345433\\volume3.*"
"C:\Program Files\7-Zip\7z.exe" e -o"D:\Temp\StackOverflow\71345433\volume4\Extract" "D:\Temp\StackOverflow\71345433\volume4\How Many Zips Was This.zip"
XCOPY /V /C /Y "D:\Temp\StackOverflow\71345433\volume4\Extract\*.*" "D:\Temp\StackOverflow\71345433\\volume4.*"

Stage 5, try it live:

        "%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C"
        XCOPY /V /C /Y "%ExtractFolder%\*.*" "%CmdPath%\%SubFolderName%.*"

What did we get?:

D:\Temp\StackOverflow\71345433>dir /s/b
D:\Temp\StackOverflow\71345433\volume1
D:\Temp\StackOverflow\71345433\volume1.pdf
D:\Temp\StackOverflow\71345433\volume2
D:\Temp\StackOverflow\71345433\volume2.pdf
D:\Temp\StackOverflow\71345433\volume3
D:\Temp\StackOverflow\71345433\volume3.pdf
D:\Temp\StackOverflow\71345433\volume4
D:\Temp\StackOverflow\71345433\volume4.pdf
D:\Temp\StackOverflow\71345433\volume5
D:\Temp\StackOverflow\71345433\volume5.pdf

Stage 6, what will remove directory look like? (Decided to hide the other commands with REM)

        REM "%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C"
        REM XCOPY /V /C /Y "%ExtractFolder%\*.*" "%CmdPath%\%SubFolderName%.*"
        ECHO;RD /Q /S "%Folder%"

And we have:

RD /Q /S "D:\Temp\StackOverflow\71345433\volume5"
RD /Q /S "D:\Temp\StackOverflow\71345433\volume1"
RD /Q /S "D:\Temp\StackOverflow\71345433\volume2"
RD /Q /S "D:\Temp\StackOverflow\71345433\volume3"
RD /Q /S "D:\Temp\StackOverflow\71345433\volume4"

Stage 7 is the final code and tests:

@ECHO OFF
    GOTO :Start

:LoopThroughParameters
    SET CmdPath=%~1
    SHIFT

:ltpLoop
    IF [%1] EQU [] GOTO :EOF
    SET Folder=%~1
    SET SubFolderName=%~nx1
    SET ExtractFolder=%~1\Extract
    
    FOR %%C IN ("%Folder%\*.zip" "%Folder%\*.rar") DO (
        "%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C"
        XCOPY /V /C /Y "%ExtractFolder%\*.*" "%CmdPath%\%SubFolderName%.*"
        RD /Q /S "%Folder%"
    )

    SHIFT
    GOTO :ltpLoop

:Start
    CALL :LoopThroughParameters "%~dp0" %*
PAUSE

All folders were deleted when done.

Now, let's do something experimental. I've never seen this fail, but I haven't used it that often. Let's give it a try:

@ECHO OFF
    GOTO :Start

:LoopThroughParameters
    SET CmdPath=%~1
    SHIFT

:ltpLoop
    IF [%1] EQU [] GOTO :EOF
    SET Folder=%~1
    SET SubFolderName=%~nx1
    SET ExtractFolder=%~1\Extract
    
    FOR %%C IN ("%Folder%\*.zip" "%Folder%\*.rar") DO (
        ( 
            "%ProgramFiles%\7-Zip\7z.exe" e -o"%ExtractFolder%" "%%~C" 
        ) && (
            XCOPY /V /C /Y "%ExtractFolder%\*.*" "%CmdPath%\%SubFolderName%.*"
        ) && (
            RD /Q /S "%Folder%"
        ) || (
            PowerShell Write-Host -ForegroundColor red "Error:"
            PAUSE
        )
    )

    SHIFT
    GOTO :ltpLoop

:Start
    CALL :LoopThroughParameters "%~dp0" %*

If any of the commands separated by && fail, then execution jumps to the command prefixed by ||. But if all of the commands separated by && succeed without throwing an error code, then the command prefixed by || is skipped. In theory, this will prevent the folder from being deleted, and pause the code, if there is problem.

So, we re-created the folders, and then used HxD to the damage zip file in volume4 folder. HxD making a PDF Bad

It worked perfectly! Folder volume4 was not deleted. Tried the test again, this time damaging the PDF in folder volume2, worked perfectly!

As you can tell, I can do things in Batch that many can't do in PowerShell, and yes @Olaf, that's not necessarily something to be proud of! ;)

  • Related