Double FOR loop to move files - batch-file

Thanks for any help in advance.
I am trying to make a batch file to move files in some folder.
Just a bit background.
I have a folder and the structure as below:
c:\test
app1\app1.exe
app2\app2.exe
app3\app3.doc
xx[xx].exe
xx2\xx2.exe
xx3\xx3.EXE
In other words, I have a folder that contains a few sub folders, each of the sub-dir has some files. What I want to do is using a script, loop all the folders, move all files contain "app" to folder app, then move all files have "xx" to folder xx - in order to minimize the effort, I've already created these two folders so the script doesn't need to decide if it has to make a new dir.
Below is my script,
#echo off
rem loop xx
FOR /r "C:\test\" %%G in (*xx*.*) DO (
Echo Found file - %%G
copy %%G c:\testf\xx\
set pathname=%%G
for %%K in ("%pathname%") do ( set filepath=%%~dpK
set filename=%%~nxK
echo Filepath is %filepath%
echo %filename% >> c:\output.log )
echo full is %pathname% )
rem loop app
FOR /r "C:\test\" %%H in (*app*.*) DO (
Echo Found file - %%H
copy %%H c:\testf\app\
set pathname=%%H
for %%L in ("%pathname%") do ( set filepath=%%~dpL
set filename=%%~nxL
echo Filepath is %filepath%
echo %filename% >> c:\output.log )
echo full is %pathname% )
Echo "All Done"
The current output log shows
xx3.EXE
xx3.EXE
xx3.EXE
app3.EXE
app3.EXE
app3.EXE
and xx files were sent to xx folder only but app files didnt.
Could I have any help to deal with this problem?
I really appreciate any help you can provide.

Perhaps the path of your app files includes spaces - that would break the COPY command unless you include quotes.
Your script is much more complicated than need be. There isn't any need for the inner loop. You can also eliminate code by using a loop to get your two strings. I would have written it like so to get the same result, with the COPY command fixed with quotes:
#echo off
for %%A in (xx app) do (
for /r "c:\test\" %%F in (*%%A*.*) do (
echo Found file - %%F
copy "%%F" "c:\testf\%%A\"
echo Filepath is %%~dpF
echo %%~nxF >>c:\output.log
echo full is %%F
)
)
echo "All Done"
Please note that copying the same file name from multiple source directories into one target directory will result in only one file - last one copied wins.

You can use FIND to test your files :
#echo off&cls
for %%a in (xx app) do call:Treat %%a
exit /b
:Treat
echo Treating : [%1]
for /f "delims=" %%a in ('dir/a-d/b/s ^| find "%1"') do echo copy "%%a" "c:\test\%1"
I put an echo before the copy that you can test the output.

Related

find specific folders with size 0 under directory using batch

can someone please help how to find specific folders with size 0 under directory using batch? thank you.
there are many folders in a parent folder, some of them have files inside and some folder are empty. I'd like to find out the name of the empty folders using batch file. can someone help? thanks
This script scans the target folder and returns the UNC paths of all empty subfolders within the target folder.
#echo off
setlocal EnableDelayedExpansion
REM This script scans the target folder and returns the UNC paths of all empty subfolders within the target folder.
title Looking for empties, please wait!
cd "%~dp0"
cls
if "%~1" == "" (
echo ERROR: No folder was specified! You gotta tell me where to look for empties!
echo Please drag-and-drop a folder onto the icon for this script.
pause
exit /b
)
if not exist "%~dpn1" (
echo ERROR: The folder you told me to scan doesn't seem to exist!
pause
exit /b
)
set "target=%~dpn1"
echo Scanning: %target%
echo.
REM Grab each subfolder for checking.
for /f "tokens=*" %%a in ('dir "%~dpn1" /s /b /a:d') do (call :checkForBlanks "%%a")
echo.
echo Done.
title Done.
pause
exit /b
:checkForBlanks
REM We've been given a folder. We must check if it's empty.
set "folder=%~1"
set "scanned=!folder:%target%=!"
title Looking for empties: %scanned:&=^&%
set exist=
for /f %%a in ('dir "%~1" /b') do (set "exist=%%a" & goto :found)
:found
if "%exist%" == "" echo EMPTY: %scanned:&=^&%
goto :eof
The following batch-file idea, which leverages powershell may provide the information you require.
I say, may, because you've not clarified what you deem to be an empty subfolder, or folder with size 0.
The following single line batch file example should work regardless of the installed powershell.exe version:
#(For /F "Delims=" %%G In ('%__AppDir__%WindowsPowerShell\v1.0\powershell.exe -NoP "(GCI -Rec|?{$_.PSIsContainer -Eq $True})|?{$_.GetFileSystemInfos().Count -Eq 0}|Select -Exp FullName" 2^>NUL')Do #Echo/%%G)&Pause
If you're using a more up to date version of powershell.exe, perhaps the following example may be preferable for you:
#(For /F "Delims=" %%G In ('%__AppDir__%WindowsPowerShell\v1.0\powershell.exe -NoP "GCI -AD -S|?{$_.GetFileSystemInfos().Count -Eq 0}|Select -Exp FullName" 2^>NUL')Do #Echo/%%G)&Pause
Both examples are designed to recurse from the current directory, if you wish to insert the base directory directly into the for loop then use it just after GCI in whichever example you choose, (preferable enclosed between single straight quotes)

Do operation if a found folder does NOT have another subfolder of the same name

I'm trying to create a batch file script that will compress all subfolders named folder1 within a directory, BUT it should only compress this folder if it does NOT contain another subfolder with the same name.
I am very new to cmd and batch files, and this is also my first post of stack overflow, please let me know if I've failed to give some information that I should!
The bit of pseudo-ish code below hopefully illustrates what I'm trying to accomplish:
#echo off
SETLOCAL EnableDelayedExpansion
FOR /D /R %%G IN (*folder1*) DO (
CD %%G
SET /A compress=true
FOR /D /R %%H IN (*folder1*) DO (
ECHO folder contains another folder of same name, should not be compressed
SET /A compress=false
)
IF !compress!==true (
ECHO Run compression operation on folder
"C:\Program Files (x86)\7-zip\7z.exe" a -tzip "%%G.zip" "%%G\"
)
)
Please ask away if anything seems unclear! I'm really hoping to turn the above into functional code, thank you in advance for any input or thoughts.
#ECHO OFF
SETLOCAL
:: Starting directory
SET "sourcedir=U:\sourcedir"
:: name of directory to compress
SET "targetname=targetdir"
FOR /d /r "%sourcedir%" %%a IN (*) DO (
IF /i "%%~nxa" == "%targetname%" IF NOT EXIST "%%a\%targetname%\." (
ECHO compress %%a
rem temporarily switch to target directory
PUSHD "%%a"
ECHO 7z a -tzip "%%a.zip"
rem back to original directory
POPD
)
)
GOTO :EOF
This should do as you want - it will echo not execute the 7z command.
The if sees whether the "name and extension" portion of the directory-name in %%a matches the target (the /i makes the match case-insensitive). If it matches AND there is a subdirectory with the required name, then the compression portion is executed.
There are two points to consider.
First, the name of the destination ZIP file. As you have written it, the ZIP generated would be folder1.zip in the parent directory. IDK what you want here. This code would do the same, but %%a.zip could be replaced by ..\%targetname%.zip since the pushd/popd changes the current directory to the folder1 directory and .. means the parent directory.
The second matter is whether or not you want to compress ...\folder1\folder1 (which would have a destination ZIP file of ...\folder1\folder1.zip)
Revision given comment:
#ECHO OFF
SETLOCAL
:: Starting directory
SET "sourcedir=U:\sourcedir"
:: name of directory to compress
SET "targetname=targetdir"
REM (
FOR /d /r "%sourcedir%" %%a IN (*) DO IF /i "%%~nxa" NEQ "%targetname%" (
rem calculate parent name in %%~nxp and grandparent in %%~nxg
FOR %%p IN ("%%~dpa.") DO FOR %%g IN ("%%~dpp.") DO (
IF /i "%%~nxp" == "%targetname%" IF /i "%%~nxg" NEQ "%targetname%" (
ECHO child %%~nxa
ECHO parent %%~nxp
ECHO Gparent %%~nxg Gppath=%%~dpg
ECHO compress %%a
rem temporarily switch to target directory
PUSHD "%%a"
ECHO 7z a -tzip "%%a.zip"
ECHO -------------------
rem back to original directory
POPD
)
)
)
GOTO :EOF
It's not that clear what you want to do with a directory named ...\folder1\folder1\something or what the target ZIP file-name should be.
The if in the for /d /r line will ensure that only leaf-names that do not match the target name are processed. The path-name in %%a is then processed into the parent and grandparent portions - note that %%~dp? is the drive+path portion of %%? which terminates \ so appending . to this resolves to effectively removing the terminal \ yielding a "filename".
You appear to want to compress directories that have a parent but not a grandparent named with the target string, hence the innermost if statement.
I've just echoed the various strings available at this point so they may be strung together as required to form your destination ZIP file-name. Note that the pushd/popd bracket ensures that the current directory at the time of the compress is the leaf to be compressed.

Batch file to copy and paste a file as many times as many files there are inside a list of folders

I'm working on a project which works like that:
take a long video hard subtitled and cut it in a lot of smaller
scenes and where each scene lasts the time that it lasts the
subtitle to disappear
cut one only picture (maybe the first one or the last one) of each
segment of the original video and do it automatically
repeat (copy and paste) the picture extracted as many times as the
number of pictures that there are on each fragment that has been cut
on the original video
join everything
I have already achieved the point 1) and 2), now I'm trying to achieve point 3) and I need your help, please. Let's say that I have achieved point 2) with this batch script (that I call a.bat) :
pushd %1
if not exist newfiles\ (
mkdir newfiles
)
if not exist newfiles2\ (
mkdir newfiles2
)
:start
for %%F in (*.mov) do (
md "%%~nF"
echo "%%~nF"
ffmpeg -i %%F -r 1 -f image2 -qscale:v 2 "%%~nF\%%~nF_image-%%3d.png"
copy "%%~nF\%%~nF_image-001.png" ".\newfiles"
)
popd
So, I have this situation:
It means that in each folder there is a variable number of images that have been extracted from the *.mov files. Now, I want that the first image on the list of the images located on the relative folders is copied in another folder, as many times as many files there are in the same folder where it is. For example, let's take a look at this folder:
Here there are 4 images, right? I want to copy the first (or the last) image of the list for 4 times in this folder:
I would like that someone can help me to improve this batch script (that I call b.bat)...
#echo off
setlocal enabledelayedexpansion
set count=0
for %%x in (*.png) do set /a count+=1
echo %count%
FOR %%F IN (*.png) DO (
set filename=%%F
goto tests
)
:tests
echo "%filename%"
for /l %%i in (1,1,%count%) do copy %filename% ..\newfiles2\%%i.png
endlocal
pause
because I figured out that it works only if I put a copy of it in every/each folder,but then I have to enter in each folder to run it,wasting a lot of time. Let's say that I want to run it in the folder where all the others folders are located. How can I modify the script to do so ? This is what happens if I run it on the "root" folder :
EDIT : thanks to the suggestion of #AlexP I modified the script like this :
#echo off
setlocal enabledelayedexpansion
set count=0
for /d %%d in (render*) do set /a count+=1t
echo %count%
pause
for %%f IN ("%%~d\*") do (
set filename=%%F
goto tests
)
:tests
echo "%filename%"
for /l %%i in (1,1,%count%) do copy %filename% .\%%i.png
endlocal
pause
now the var "count" has value 4,but this is wrong. the value that i need is inside each folder that starts with the word render* ; so,since I have 4 pics inside the "render00003211" folder the value is 4; 5 pics under "render00003266" the value is 5 and so on..
UPDATE : I've ordered sequentially the names of the pictures by placing this script inside the Duplicates folder...
#echo off
setlocal enabledelayedexpansion
mkdir pics
copy *.png .\pics
del *.png
for /d /r %%a in (%1\*.*) do (
set /a counter=0
for %%b in ("%%a\*.*") do (
set /a counter += 1
set zcounter=0000!counter!
set source="%%~fb"
set target="pic!zcounter:~-4!%%~xb"
echo Renaming !source! to !target!
ren !source! !target!
)
)
for /l %%i in (1,1,%count%) do copy %filename% %somewhere%
From for /?:
FOR /L %variable IN (start,step,end) DO command [command-parameters]
The set is a sequence of numbers from start to end, by step amount. So (1,1,5) would generate the sequence (1 2 3 4 5) and (5,-1,1) would generate the sequence (5 4 3 2 1).
Here is a script which implements what I understand to be the requirements:
#echo off
:: This script will visit each subdirectory of the directory from where it is
:: run; it will take the first file found in that subdirectory and copy it to
:: the directory ..\Duplicates as many times as there are files in that
:: subdirectory; the names of the copied files are taken from the name of the
:: subdirectory with a numeric suffix appended, and have the extension of the
:: copied file. If ..\Duplicates does not exist then it is created.
setlocal
echo Working in directory "%CD%"
if not exist ..\Duplicates mkdir ..\Duplicates
if not exist ..\Duplicates\. (
echo Destination ..\Duplicates does not exist, and I cannot create it.
exit /b
)
for /d %%d in (*) do call :duplicateDir "%%~d"
exit /b
:duplicateDir
echo * "%~1"
set COUNT=0
set "SUBNAME=%~n1"
set SUBFILE=&
set SUBEXT=&
for %%f in ("%~1\*") do call :duplicateFile "%%~f"
echo "%SUBFILE%" copied %COUNT% times
exit /b
:duplicateFile
if not defined SUBFILE set "SUBFILE=%~1"
if not defined SUBEXT set "SUBEXT=%~x1"
set /a COUNT=COUNT+1
echo copy "%SUBFILE%" "..\Duplicates\%SUBNAME%-%COUNT%%SUBEXT%"
:: copy "%SUBFILE%" "..\Duplicates\%SUBNAME%-%COUNT%%SUBEXT%" >nul
exit /b
Note that instead of actually doing the copying the script displays the command which would be executed. When you are happy with it, comment out the echo copy and uncomment the copy.

Moving files from various folder in to single folder based on the list

I have a relatively simple task to do. I have to move 100 files which are located in various different folders, into one single folder. So far I have the script that will do this if i am moving the files from one folder to another folder based on the list of files.
#echo off
set Source=C:\IMAGES_SOURCE
set Target=C:\IMAGE_DESTINATION
set FileList=C:\ImageList.txt
echo.
if not exist "%Source%" echo Source folder "%Source%" not found & goto Exit
if not exist "%FileList%" echo File list "%FileList%" not found & goto Exit
if not exist "%Target%" md "%Target%"
for /F "delims=" %%a in ('type "%FileList%"') do copy "%Source%\%%a"
"%Target%"
:Exit
echo.
echo press the Space Bar to close this window.
pause > nul
So for this instance, I created the list of the items and scrip will look for the file name in source location and once find them it will move them to a destination location.
I need to adjust this so it reads the names of the files and try to find them in the list of the folders that they might be.
What i have tried is to change the set Source to be also separate list that will contain posible folder names:
set Source=C:\DirList.txt
But when i do this it does not copy the images.
Any idea how to do this?
First thing that comes to mind is dir /S meaning it will search using the dir command. This is untested, but you'll get the idea:
#echo off
cd /d C:\Images_source
setlocal enabledelayedexpansion
for /F "delims=" %%i in (C:\Imagelist.txt) do (
set var="%%i"
for /F "delims=" %%a in ('dir /B /S !var!') do echo copy "%%~fa" C:\Image_Destination
)
For more on the commands I used, try help by running from from commandline for /? and dir /?
Once confident that it will copy only what you want to copy/move, then remove the echo before copy.

Dynamic Locate All Files and replace with specific file script

I've been trying to get the syntax right, but I'm having issues. Not only am I not getting the syntax right, but I would like this script to:
Locate all files of a certain criteria on All Drives in a network
check to see if the file is the updated file (or new file)
if it is NOT the new or updated file, locate the new file and replace it
ALSO! If I can get this to work on a schedule, such as every 6 hours... that would be a real help
I got this code to work once, but I changed it a couple times and saved over it.
#echo off
SETLOCAL
cls
:locate_old
for /d /r Z:\ %%a in (*) do if exist "%%~fa\old.file" set "oldFile=%%~fa\old.file"
if not defined oldFile (
echo old file not found...
) else (
echo old file found&GOTO oldFileCheck
)
:oldFileCheck
find "old file text" "%oldFile%" && echo old file is already updated || GOTO findNewFile
:findNewFile
for /d /r Z:\ %%a in (*) do if exist "%%~fa\new.file" set "newFile=%%~fa\new.file"
if not defined newFile (
echo no new file detected...
) else (
echo new file located...&GOTO fileSwap
)
:fileSwap
copy /y "%newFile%" "%oldFile%" && echo file updated || file not updated
If I understand your requirement, you want to replace all "old.file" files on Z:\ with "new.file" files if they aren't updated already. This is untested:
#if not defined debug_batch_files echo off
REM You can set debug_batch_files to 1 and quickly see verbose execution
pushd Z:\
for /f "delims=" %%a in ('dir /b /s /a-d new.file') do echo xcopy /D /Y "%%~fa" "%%~dpaold.file"
REM Remove "echo" from the above line if it displays the right paths.
popd

Resources