I have a list of folders and files in a text file, (%File_list%). My batch reads from the text file for the objects to copy. I need it to copy files and folders from one within a folder (%src_folder%), to another folder, (%dst_folder%).
My current attempt copies the files, but not the folders.
Batch script:
#echo off
set src_folder=C:\Users\P57949\Desktop
set dst_folder=C:\Users\P57949\Desktop\NewTestFolder
set file_list=C:\Users\P57949\Desktop\Filelist.txt
if not exist "%dst_folder%" mkdir "%dst_folder%"
for /f "delims=" %%f in (%File_list%) do (
xcopy /y /s "%src_folder%\%%f" "%dst_folder%\"
)
Text file:
Fixed Retail.xlsx
SecureFolder
Can someone please advise what I am doing wrong? How can I copy folders as well as file?
When the source is a directory, the xcopy command copies its contents rather than the whole directory. To copy the whole directory, you need to alter the destination accordingly:
xcopy /I /Y /S "%src_folder%\%%f" "%dst_folder%\%%f"
Note the /I option, that tells xcopy that the destination is not a file but a directory.
However, when the source is a single file, xcopy prompts you whether the destination is a file or directory when the destination does not yet exist, even when /I is provided (refer to this related post). Therefore, we have to determine what the source (%%f) is in advance and react adequately:
#echo off
rem // The quoted `set` syntax protects special characters:
set "src_folder=C:\Users\P57949\Desktop"
set "dst_folder=C:\Users\P57949\Desktop\NewTestFolder"
set "file_list=C:\Users\P57949\Desktop\Filelist.txt"
rem // Append `\*` to check a directory but not a file for existence:
if not exist "%dst_folder%\*" mkdir "%dst_folder%"
rem // Use `usebackq` option and quotation to permit spaces in file path:
for /F "usebackq delims=" %%f in ("%File_list%") do (
rem // Check whether source is a directory:
if exist "%src_folder%\%%f\*" (
rem // Source is a directory, so copy it entirely:
xcopy /I /Y /S "%src_folder%\%%f" "%dst_folder%\%%f"
) else (
rem // Source is a file, or it does not exist:
xcopy /Y /S "%src_folder%\%%f" "%dst_folder%\"
)
)
A bit simpler and more compact approach can be achieved when a destination file is pre-created, so the file/directory prompt does not appear:
#echo off
rem // The quoted `set` syntax protects special characters:
set "src_folder=C:\Users\P57949\Desktop"
set "dst_folder=C:\Users\P57949\Desktop\NewTestFolder"
set "file_list=C:\Users\P57949\Desktop\Filelist.txt"
rem // Append `\*` to check a directory but not a file for existence:
if not exist "%dst_folder%\*" mkdir "%dst_folder%"
rem // Use `usebackq` option and quotation to permit spaces in file path:
for /F "usebackq delims=" %%f in ("%File_list%") do (
rem // Pre-create destination file when source is a file:
if not exist "%src_folder%\%%f\*" > "%dst_folder%\%%f" rem/
rem // No prompt appears even when source is a file:
xcopy /I /Y /S "%src_folder%\%%f" "%dst_folder%\%%f"
)
The easiest approach is to use the /I option to cover the case when source is a directory and to auto-fill the file/directory prompt for the case source is a file, but this is locale-dependent (for instance, File and Directory for English systems, but Datei and Verzeichnis for German ones), so be careful:
#echo off
rem // The quoted `set` syntax protects special characters:
set "src_folder=C:\Users\P57949\Desktop"
set "dst_folder=C:\Users\P57949\Desktop\NewTestFolder"
set "file_list=C:\Users\P57949\Desktop\Filelist.txt"
rem // Append `\*` to check a directory but not a file for existence:
if not exist "%dst_folder%\*" mkdir "%dst_folder%"
rem // Use `usebackq` option and quotation to permit spaces in file path:
for /F "usebackq delims=" %%f in ("%File_list%") do (
rem // The `/I` option copes for directories, `echo F` for files:
rem echo F| xcopy /I /Y /S "%src_folder%\%%f" "%dst_folder%\%%f"
)
N. B.:
For all of the above, I assumed that the list file Filelist.txt contains pure file/directory names.
Related
I am trying to copy the listed files in file_list.txt to destination folder from source location, source folder has subfolders. my batch should be capable to search the file in source subfolders and copy to destination folder. same for copy all files with extension .exe. what is wrong with my code. I think, I have missed to search subfolders data. don't know what is the command. please help.
#Echo Off
SETLOCAL ENABLEDELAYEDEXPANSION
#color 0a
cls
set "dest=D:\destination"
set /p source=Select source path:
for /R %%f in (%source%/*.exe) do copy %%f "%dest%"
echo Copy requested files
for /f %%f in (file_list.txt) do (
for /f "tokens=*" %%F IN ('dir /S /B /A:-D "%source%/%%f"') Do (
copy "%%F" "%dest%\%USERNAME%"
)
)
pause
ENDLOCAL
I cannot see anything obviously wrong with the code you've posted, although there are possibilities for errors. You have not provided sufficient information, about your current directory, the content of the text file, how the script is invoked or any debugging information.
The following version of your code, requires that you put your file listing text file into the variable definition on line five. I've assumed that the batch file is in the same location as the file listing, and therefore used %~dp0. If it is in the current directory instead, then replace that with %CD%\, and obviously the fully qualified absolute path if neither.
Next I use some validation to try to ensure that the source, destination, and user input exist. The input location will then become the current directory.
Your provided commands are then run, before the curent directory is returned to its original location.
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Color 0A
Set "listdir=%~dp0filelist.txt"
Set "dest=D:\destination"
If Not Exist "%listdir%" GoTo :EOF
If Not Exist "%dest%\" GoTo :EOF
:Ask
ClS
Set "source="
Set /P "source=Select source path: "
If Not Defined source GoTo Ask
PushD "%source:"=%" 2> NUL || GoTo Ask
For /R %%G In (*.exe) Do Copy /Y "%%G" "%dest%"
Echo Copy requested files
For /F "UseBackQ EOL=? Delims=" %%G In ("%listdir%"
) Do For /F "Delims=" %%H In ('Dir "%%~nxG" /S /B /A:-D 2^> NUL'
) Do Copy /Y "%%H" "%dest%\%UserName%"
PopD
Pause
Feel free to try the code, and provide some proper debugging information if it still fails to work as intended.
I am trying to create a batch script to move files base on search criteria into another folder of the same subfolder structure.
I tried the following but the result is not quite right.
for /r "c:\Test_Copy\Source1\" %%x in (Test*.txt) do move "%%x" "c:\Test_Copy\Target1\"
As it is showing
move "c:\Test_Copy\Source1\Sub1\Test1.txt" "c:\Test_Copy\Target1\"
move "c:\Test_Copy\Source1\Sub2\Test1.txt" "c:\Test_Copy\Target1\"
I would like the outcome to be the following.
move "c:\Test_Copy\Source1\Sub1\Test1.txt" "c:\Test_Copy\Target1\Sub1\Test1.txt"
move "c:\Test_Copy\Source1\Sub2\Test1.txt" "c:\Test_Copy\Target1\Sub2\Test1.txt"
How will I be able to achieve this?
Thanks
The for /R loop returns full absolute paths, even the ~-modifiers do not allow to return relative paths. However, you could use xcopy /L, which just lists files that it would copy without the /L option, with paths relative to the source root directory; that list can easily be captured and processed by a for /F loop:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=C:\Test_Copy\Source1"
set "_DESTIN=C:\Test_Copy\Target1"
rem // Change into source root directory:
pushd "%_SOURCE%" && (
rem /* Run `xcopy /L` to list files but not actually copy any, because this returns
rem paths relative to the source root directory; then let `for /F` capture that
rem list, split off the preceding drive letter and skip the summary line: */
for /F "tokens=2 delims=:" %%F in ('
xcopy /L /S /Y "Test*.txt" "%TEMP%"
') do (
rem // Create potentially needed sub-directory, suppress errors when it exists:
2> nul md "%_DESTIN%\%%F\.."
rem // Actually move the currently iterated file into the destination directory:
move "%%F" "%_DESTIN%\%%F"
)
rem // Return from source root directory:
popd
)
endlocal
exit /B
The great advantage of this method is that no string manipulation is involved to derive relative paths, which is dangerous as it could fail in certain situations (for instance, when a root path like D:\ is given, or when a path like D:\some\.\source\dummy\..\folder is specified).
Of course you can also use robocopy /L as suggested in a comment by user Mofi:
robocopy "C:\Test_Copy\Source1" "C:\Test_Copy\Target1" "Test*.txt" /S /MOV /NDL /NJH /NJS
#echo off
for /D /R "c:\Test_Copy\Source1\" %%I in (*)do pushd "%%~I" & (
2>nul >nul mkdir "c:\Test_Copy\Target1\%%~nxI"
move "%%~I\Test*.txt" "c:\Test_Copy\Target1\%%~nxI" & popd
)
Outputs command loop results:
move "c:\Test_Copy\Source1\Sub1\test_001.txt" "c:\Test_Copy\Target1\Sub1\test_001.txt"
move "c:\Test_Copy\Source1\Sub1\test_002.txt" "c:\Test_Copy\Target1\Sub1\test_002.txt"
move "c:\Test_Copy\Source1\Sub1\test_003.txt" "c:\Test_Copy\Target1\Sub1\test_003.txt"
move "c:\Test_Copy\Source1\Sub2\test_001.txt" "c:\Test_Copy\Target1\Sub2\test_001.txt"
move "c:\Test_Copy\Source1\Sub2\test_002.txt" "c:\Test_Copy\Target1\Sub2\test_002.txt"
move "c:\Test_Copy\Source1\Sub2\test_003.txt" "c:\Test_Copy\Target1\Sub2\test_003.txt"
The move command results:
c:\Test_Copy\Source1\Sub1\test_001.txt
c:\Test_Copy\Source1\Sub1\test_002.txt
c:\Test_Copy\Source1\Sub1\test_003.txt
3 file(s) moved.
c:\Test_Copy\Source1\Sub2\test_001.txt
c:\Test_Copy\Source1\Sub2\test_002.txt
c:\Test_Copy\Source1\Sub2\test_003.txt
3 file(s) moved.
Obs.: If all your subfolders c:\Test_Copy\Target1\Sub[n] already exist, remove line 2>nul >nul mkdir "c:\Test_Copy\Target1\%%~nxI"
#echo off
For /D /R "c:\Test_Copy\Source1\" %%I in (*)do pushd "%%~I" & (
move "%%~I\Test*.txt" "c:\Test_Copy\Target1\%%~nxI" & popd
)
Try using for /D /R:
FOR /R - Loop through files (recursively)
FOR /D - Loop through several folders/directories
The option /D /R is undocumented, but can be a useful combination, while it will recurse through all subfolders the wildcard will only match against Folder/Directory names (not filenames)
Source linked to ss64.com
Rem :: Set your folder /Directory /Recursively tree starting at "c:\Test_Copy\Source1"
For /D /R "c:\Test_Copy\Source1"
If you want to do it with batch you need to modify the value of %%x to point to the target directory INSIDE the loop. When you do this you can NOT use % to surround the variables you create inside the for loop - you have to use delayed expansion with ! to surround those variables. Then you can use variable replacement on %%x to change it's value.
Like the comments say, this doesn't work if your directory/file names contain more than one exclamation point.
This does what you want I think:
#echo off
setlocal enabledelayedexpansion
set sourcedir=c:\Test_Copy\Source1\
set targetdir=c:\Test_Copy\Target1\
for /r "%sourcedir%" %%x in (Test*.txt) do (
set sourcefile=%%x
set destfile=!sourcefile:%sourcedir%=%targetdir%!
echo move !sourcefile! !destfile!
)
Just change echo move to move when you are ready to actually do the move.
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.
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%"
I'm trying to run a bat file that:
creates a folder
searches for all files with .log extension
Copies and rename the files to MyFile_customString.log to another folder
So far I did this:
#echo off
set host_name=%1
set origin_path=%2
set destiny_path=%3
set destiny_host_path=%destiny_path%\%host_name%\
mkdir .\%destiny_host_path%
FOR %%G IN (%origin_path%/*.log) DO (
SET _fileName=%%G
SET _custom=%_fileName:.log=_%%host_name%.log%
xcopy /Y /F %origin_path%\%_fileName% %destiny_host_path%\%_custom%
)
And having MyTest.log and MyTest2.log files in origin_path only MyTest2.log file is copied to destiny_host_path
What am I missing?
You need SetLocal EnableDelayedExpansion at top of batch file after #echo
In your code:
FOR %%G IN (%origin_path%/*.log) DO (
SET _fileName=%%G
SET _custom=%_fileName:.log=_%%host_name%.log%
xcopy /Y /F %origin_path%\%_fileName% %destiny_host_path%\%_custom%
)
should be:
FOR %%G IN (%origin_path%/*.log) DO (
SET "_fileName=%%G"
FOR %%H in (!host_name!) Do (
SET "_custom=!_fileName:.log=_%%H.log!"
)
xcopy /Y /F !origin_path!\!_fileName! !destiny_host_path!\!_custom!
)
FOR %%G IN (%origin_path%\*.log) DO (
xcopy /Y /F "%origin_path%\%%G" "%destiny_host_path%\%%~nG_%host_name%.log"
)
(untested)
No need for delayedexpansion in this case.
See for /? |more from the prompt for information on the use of ~-modifiers to extract parts of filenames.
In your original, your %s were unbalanced (assuming that % had been correct in that context) in the set _custom line.
Tip: replace xcopy with echo xcopy temporarily to show rather than execute the command - easier to debug in case of an error...