Copying multiple files using DOS - dos

I have different folders eg: a,b,c etc and each folder has different files eg: x,y,z etc.
The files x,y,z is in each folder. Is there a way to copy each file from different folders using a single command? I want the result to be x file from all the folders to be in one folder.
Eg:xcopy/S x*.* C:\Folder
which can copy all the x file from different folders. How can I copy x,y,z from different folders using a single command?. Thanks

You have to use 'for' either in a batch file or directly from the cmd.
for %I in (file1.txt file2.txt file3.txt) do xcopy\s %I c:\somedir\
Instead of file name you could give the absolute/relative path for different files in different folder

There is no way to do this with a single command in DOS. What you need is a script like the one below. This can be executed in the root folder of the tree you are trying to collect from, like this:
batch.bat x*.*
It will harvest all the files matching x*.* in the current directory and all subdirectories, recursively. They will be saved in this format:
folder1\x1.txt - > folder1x1.txt
folder2\x5.txt - > folder2x5.txt
folder3\x1.txt - > folder3x1.txt
Here is the code:
#echo off
FOR /F "delims=" %%I IN ('DIR .\%1 /B /AA /S') DO (CALL :COPYFILE %%I)
GOTO END
:COPYFILE
set "ofn=%~1"
set "fn=%~1"
call:MakeRelative fn
set fn=%fn:\=%
set fn=%fn::=%
copy %ofn% %fn%
goto:eof
:MakeRelative file base -- makes a file name relative to a base path
:: -- file [in,out] - variable with file name to be converted, or file name itself for result in stdout
:: -- base [in,opt] - base path, leave blank for current directory
:$created 20060101 :$changed 20080219 :$categories Path
:$source http://www.dostips.com
SETLOCAL ENABLEDELAYEDEXPANSION
set src=%~1
if defined %1 set src=!%~1!
set bas=%~2
if not defined bas set bas=%cd%
for /f "tokens=*" %%a in ("%src%") do set src=%%~fa
for /f "tokens=*" %%a in ("%bas%") do set bas=%%~fa
set mat=&rem variable to store matching part of the name
set upp=&rem variable to reference a parent
for /f "tokens=*" %%a in ('echo.%bas:\=^&echo.%') do (
set sub=!sub!%%a\
call set tmp=%%src:!sub!=%%
if "!tmp!" NEQ "!src!" (set mat=!sub!)ELSE (set upp=!upp!..\)
)
set src=%upp%!src:%mat%=!
( ENDLOCAL & REM RETURN VALUES
IF defined %1 (SET %~1=%src%) ELSE ECHO.%src%
)
EXIT /b
:END
Hope this helps!

Related

Batch: Looping through folders and subfolders and get them into a script

I want to convert .flac files from X:\Music\flac\flacfolder\name.flac to X:\Music\mp3\flacfolder\name.mp3 using ffmpeg, but I couldn't find how to loop through while passing the directory to a different command and manipulating it.
Usually, to process files recursively, I would suggest to use the for /R loop. However, in this situation, since I guess you want to copy the directory hierarchy from the source to the target folder, I do not use it, because it resolves to absolute paths only. Instead I use xcopy /L, which does not copy anything (due to /L), but lists all applicable items as paths relative to the source folder; then I wrap around a for /F loop to read the list of relative paths and to resolve them related to the target folder; in the loop body finally, the ffmpeg needs to be placed (define the options to your needs and remove the preceding upper-case ECHO after having tested; the ffmpeg tool does not receive any relative paths but absolute ones only for both input and output files):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=X:\Music\flac\flacfolder" & rem // (absolute source path)
set "_TARGET=X:\Music\mp3\flacfolder" & rem // (absolute target path)
set "_PATTERN=*.flac" & rem // (pure file pattern for input files)
set "_FILEEXT=.mp3" & rem // (pure file extension of output files)
pushd "%_TARGET%" || exit /B 1
for /F "delims=" %%F in ('
cd /D "%_SOURCE%" ^&^& ^(rem/ list but do not copy: ^
^& xcopy /L /S /Y /I ".\%_PATTERN%" "%_TARGET%" ^
^| find ".\" ^& rem/ remove summary line;
^)
') do (
2> nul mkdir "%%~dpF."
rem // Set up the correct `ffmpeg` command line here:
ECHO ffmpeg -i "%_SOURCE%\%%~F" "%%~dpnF%_FILEEXT%"
)
popd
endlocal
exit /B
If you want the destination files in a flat folder structure instead, a for /R loop workes fine:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=X:\Music\flac\flacfolder" & rem // (absolute source path)
set "_TARGET=X:\Music\mp3\flacfolder" & rem // (absolute target path)
set "_PATTERN=*.flac" & rem // (pure file pattern for input files)
set "_FILEEXT=.mp3" & rem // (pure file extension of output files)
for /R "%_SOURCE%" %%F in ("%_PATTERN%") do (
rem // Set up the correct `ffmpeg` command line here:
ECHO ffmpeg -i "%_SOURCE%\%%~F" "%_TARGET%\%%~nF%_FILEEXT%"
)
endlocal
exit /B
Try something like:
#echo off
setlocal
set FLAC_FOLDER=c:\temp\flacfolder
set MP3_ROOT_FOLDER=c:\temp\mp3folder
echo Processing folder [%FLAC_FOLDER%]...
for /f "tokens=*" %%F in ('dir "%FLAC_FOLDER%\*.flac" /a-d /b') do call :PROCESS_FLAC_FILE "%FLAC_FOLDER%" "%%F"
goto END
:PROCESS_FLAC_FILE
set PFF_FOLDER=%1
set PFF_FILE=%2
set PFF_FOLDER=%PFF_FOLDER:"=%
set PFF_FILE=%PFF_FILE:"=%
for /f %%I in ("%PFF_FILE%") do set PFF_MP3_FILE=%%~nI.mp3
echo Processing FLAC file [%PFF_FILE%] in folder [%PFF_FOLDER%]; output file is [%PFF_MP3_FILE%]...
REM Now call ffmpeg using the approriate variables. Enclose the variables in double-quotes, e.g.:
REM (note, I don't know the syntax for ffmpeg, so I'm making this up as an example)
ffmpeg.exe -source "%PFF_FOLDER%\%PFF_FILE%" -target "%MP3_ROOT_FOLDER%\%PFF_MP3_FILE%"
goto END
:END
I have an example I used in the past. I tried adding your structure to it.
#echo off
cd X:\Music\flac\flacfolder
for /F "tokens=1 delims=" %%i IN ('dir /s /b ^| findstr .flac') do (
call :process_code "%%i"
)
goto end
:process_code
echo Running conversion for %1
:: Run your process here
goto :eof
:end
echo done!
I hope this helps

Find all files and copy to another destination keeping the folder structure

I have a folder structure with a bunch of *.jpg files scattered across the folders.
Now I want to find some files listed in a CSV file (one column only) or a text file line by line like
one.jpg
ten.jpg
five.jpg
and copy all those *.jpg files to another folder keeping the folder structure like:
outputfolder\somefolder\somefolder\one.jpg
outputfolder\somefolder\ten.jpg
outputfolder\somefolder\somefolder\somefolder\five.jpg
How can I achieve this?
Here is what I've tried
#echo off
CLS
REM CHECK FOR ADMIN RIGHTS
COPY /b/y NUL %WINDIR%\06CF2EB6-94E6-4a60-91D8-AB945AE8CF38 >NUL 2>&1
IF ERRORLEVEL 1 GOTO:NONADMIN
DEL %WINDIR%\06CF2EB6-94E6-4a60-91D8-AB945AE8CF38 >NUL 2>&1
:ADMIN
REM GOT ADMIN RIGHTS
COLOR
1F ECHO Please wait...
FOR /R "%~dp0" %%I IN (.) DO
for /f "usebackq delims=" %%a in ("%~dp0list.txt") do
echo d |xcopy "%%I\%%a" "C:\B2B_output_files" /e /i
COLOR 2F
PAUSE
GOTO:EOF
:NONADMIN
REM NO ADMIN RIGHTS
COLOR 4F
pause
GOTO:EOF
Stack Overflow is not a free code writing service, see help topic What topics can I ask about here?
However, I have nevertheless written the entire batch code for this task. Learn from this commented code and next time try to write the batch code by yourself and ask only if you stick on a problem you can't solve by yourself after several trials and not finding a solution on Stack Overflow or any other website.
The paths of source and target base folder must be defined at top of the batch script below.
The text file containing the name of the files to copy line by line must be named FileNames.txt and must be stored in source base folder with using batch code below.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Define source and target base folders.
rem Note:
rem The paths should not contain an equal sign as then the
rem string substitutions below would not work as coded. The
rem target base folder can be even a subfolder of the source
rem base folder.
set "SourceBaseFolder=C:\Temp"
set "TargetBaseFolder=C:\Temp\OutputFolder"
rem Set source base folder as current directory. The previous
rem current directory is restored by command endlocal at end.
if not exist "%SourceBaseFolder%\*" (
echo %~nx0: There is no folder %SourceBaseFolder%
set "ErrorCount=1"
goto HaltOnError
)
cd /D "%SourceBaseFolder%"
if not exist "FileNames.txt" (
echo %~nx0: There is no file %SourceBaseFolder%\FileNames.txt
set "ErrorCount=1"
goto HaltOnError
)
rem For each file name in text file FileNames.txt in
rem source base folder the loops below do following:
rem 1. Search recursively for a file with current file name
rem in entire directory tree of source base folder.
rem 2. If a file could be found, check its path. Skip the
rem file if the path of found file contains the target
rem folder path to avoid copying files to itself. This
rem IF condition makes it possible that target base
rem folder is a subfolder of source base folder.
rem 3. Create the folders of found file relative to source
rem base path in target base folder. Then check if this
rem was successful by verifying if the target folder
rem really exists and copy the file on existing folder or
rem output an error message on failure creating the folder.
set "ErrorCount=0"
for /F "usebackq delims=" %%N in ("FileNames.txt") do (
for /R %%J in ("%%N*") do (
set "FilePath=%%~dpJ"
if "!FilePath:%TargetBaseFolder%=!" == "!FilePath!" (
set "TargetPath=%TargetBaseFolder%\!FilePath:%SourceBaseFolder%\=!"
md "!TargetPath!" 2>nul
if exist "!TargetPath!\*" (
echo Copying file %%~fJ
copy /Y "%%~fJ" "!TargetPath!" >nul
) else (
set /A ErrorCount+=1
echo Failed to create directory !TargetPath!
)
)
)
)
:HaltOnError
if %ErrorCount% NEQ 0 (
echo.
pause
)
endlocal
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.
call /? ... for an explanation of %~nx0
copy /?
echo /?
endlocal /?
for /?
goto /?
if /?
md /?
pause /?
rem /?
set /?
setlocal /?
And read also the Microsoft article about Using command redirection operators to understand 2>nul for suppressing error messages written to STDERR.
The following code should do what you asked for:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LISTFILE=copylist.txt"
set "SOURCE=.\source"
set "DESTIN=.\destin"
for /F "usebackq eol=| delims=" %%L in ("%LISTFILE%") do (
for /F "eol=| delims=" %%F in ('
cd /D "%SOURCE%" ^
^& xcopy /L /S /I /Y ".\%%~L*" "%TEMP%" ^
^| findstr /I /R "^\..*\\%%~L$"
') do (
2> nul md "%DESTIN%\%%F\.."
copy /B /-Y "%SOURCE%\%%F" "%DESTIN%\%%F"
)
)
endlocal
exit /B
It relies on the fact that xcopy outputs relative paths to the console if a relative source path is given, and that it features a switch /L that tells it not to copy anything but list what would be copied without the switch. There is also the switch /S which defines to search for the source item recursively also within subdirectories.
There is a small problem though which requires to be worked around: xcopy /S only walks through subdirectories if source contains a wildcard * or ?, but not if a dedicated file name is given. That is why * is appended to the file name. Since this could also match some unintended items of course, findstr is used to filter them out.
So basically there is a for /F loop that iterates through the items listed in the text file copylist.txt. Within this loop another for /F is nested that enumerates the output of the aforementioned findstr-filtered xcopy /L /S output, which receives the items of the outer loop one after another. The embedded cd command ensures to be in the source directory. The destination of xcopy is just an existing directory to avoid error messages (remember nothing is actually copied due to /L).
The inner loop bopy contains an md command that creates the destination directory (tree), if not existing (2> nul avoids error messages if has already been created earlier), and a copy command which actually performs the copying activity; the switch /-Y defines to prompt the user in case an item already exists at the destination location, you can change it to /Y to overwrite without asking.

If names match, take file with longer name

I have some batch script code to recursively copy the contents of a folder and its sub-folders into a singular folder :
for /r %f in (*) do #copy "%f" .
I would like to change the behavior slightly, so that if two or more files have the same name, I would like to copy only the one which has the longest file path (in terms of how many levels deep it is, not the character length).
eg if we had :
C:/Desktop/Folder/File1
and
C:/Desktop/Folder/NewFolder/File1
in this case, we would take the second file and copy it into the new folder.
I don't believe this is possible with a batch script, though I may be incorrect in this.
Thanks.
you may be already there, because FOR /R will recurse in the same branch from top to bottom, you will end with the deepest file just by force overwriting previously copied files
for /r %f in (*) do #copy /Y "%f" \destination
The copy operations in this code are only echoed to console. If the output is correct, remove the echo command before copy
#echo off
setlocal enableextensions disabledelayedexpansion
:: CONFIGURATION
rem Source and target folders for copy operation
set "sourceFolder=%cd%"
set "targetFolder=x:\somewhere"
rem Temporary files needed
set "tempList=%temp%\%~nx0.%random%%random%.tmp"
set "endList=%tempList%.end"
rem Variable used for level padding
set "level=10000"
:: SEARCH FILES
rem For each folder under the indicated source
(for /r "%sourceFolder%" /d %%a in (.) do (
rem Retrieve the deep level splitting the folder name
rem and counting the segments
set "folder=%%a"
setlocal enabledelayedexpansion & for %%b in ("!folder:\=" "!") do set /a "level+=1"
rem For each file in the folder output filename, level and full file path
rem A pipe character is used as delimiter between the tokens
for %%b in (!level!) do (
endlocal
for %%c in ("%%~fa\*") do (echo(^|%%~nxc^|%%b^|%%~fc)
)
rem And send everything to a temporary file
)) > "%tempList%"
rem Sort the temporary file on filename and level in descending order.
sort /r "%tempList%" /o "%endList%"
rem Now for each group of files with same name, the first one is the
rem file located in the deepest folder
:: COPY FILES
rem No file has been copied
set "lastFile=|"
rem For each record in the sorted file list, split it in three tokens
rem using the included pipe charactes as delimiters
for /f "usebackq tokens=1-3 delims=|" %%a in ("%endList%") do (
setlocal enabledelayedexpansion
for %%d in ("!lastFile!") do (
endlocal
rem If the name of the file is different to
rem the previous one, we have started a new group and as the first file
rem is the deepest one, copy it and store the new file name
if not "%%a"=="%%~d" (
echo copy /y "%%c" "%targetFolder%"
set "lastFile=%%a"
)
)
)
:: CLEANUP
rem Remove the temporary files
del /q "%tempList%"
del /q "%endList%"

Find file and return full path using a batch file

Is it possible to create a batch file that searches for a file name, then returns its path so I can use it in a variable?
for /r C:\folder %%a in (*) do if "%%~nxa"=="file.txt" set p=%%~dpnxa
if defined p (
echo %p%
) else (
echo File not found
)
If the file you searched for was found it will set the variable %p% to the full path of the file including name and extension.
If you just want the path (as in the folder path without the file) then use set p=%%~dpa instead.
Note: If there is more than 1 file with the same name then the variable will be set to the last one found. Also the script after the for loop line isn't really necessary, just to show you if it found anything :)
If you want to do it using the dir command then use this, same rules apply
for /f "tokens=*" %%a in ('dir acad.exe /b /s') do set p=%%a
Do this: -
for /f "delims=" %%F in ('dir /b /s "C:\File.txt" 2^>nul') do set MyVariable=%%F
Assuming you are searching C drive for File.txt .

Moving and renaming in batch

How to write a Batch program that can move files with .txt from a folder (including files in sub-folder) in to a different folder and rename it in the form folderName_subfolderName_Filename.extension
This following snippet should do the trick. Modify it to your needs.
#ECHO OFF
REM Put the source and destination folde names here.
REM You can use %1 and %2 instead if you want to pass
REM folders as command line parameters
SET SOURCE_FOLDER=C:\SRC
SET TARGET_FOLDER=C:\DST
REM This is needed for variable modification inside the FOR loop
SETLOCAL ENABLEDELAYEDEXPANSION
REM The FOR loop lists all files recursively beginning in
REM %SOURCE_FOLDER% matching the *.txt pattern.
REM Filenames can be accessed in th loop via the %%F variable
FOR /R %SOURCE_FOLDER% %%F IN (*.txt) DO (
REM Put the path and filename into the FILE_NAME variable
SET FILE_NAME=%%~pnxF
REM Transform the path to new filename
REM (replace '\' with '_' and strip the first '\')
SET FILE_NAME=!FILE_NAME:\=_!
SET FILE_NAME=!FILE_NAME:~1!
REM This is the actual MOVE command creating the
REM targest filename from the variables.
MOVE "%%F" "%TARGET_FOLDER%\!FILE_NAME!"
)
adopted solution:
usage: moveit TargetFolder DestinationFolder NameOfTargetFolder
Sample: moveit C:\MyFolder C:\MySecondFolder MyFolder
moveit.bat:
Set target=%~1
Set destination=%~2
Set prefix=%~3
for /f "tokens=*" %%f in ('dir /b %target%\*.txt') do move "%target%\%%f" "%destination%\%prefix%_%%f"
for /f "tokens=*" %%s in ('dir /b/ad %target%\*') do call moveit.bat "%target%\%%s" "%destination%" %prefix%_%%s

Resources