Batch Function Not Copying Properly - windows

REM Capture the date/time(right down to the second) and then assign it to a variable
set yy=%date:~-4%
set dd=%date:~-7,2%
set mm=%date:~-10,2%
set newdate=%dd%%mm%%yy%_%Time:~0,8%
set newdate=%newdate::=%
SET foldername="svetlana_backup_%newdate%"
SET drive=T:
REM Source directories
SET documents=%drive%\Documents
REM Destination directories
SET destinationDocuments=%backupDir%\Documents
call:makedirandcopy %documents% %destinationDocuments%
:makedirandcopy
ECHO Making and Copying %~2 Directory
MKDIR %~2
XCOPY %~1 %~2 /E /F
I have the following batch file on my desktop, when I run the batch file, it supposed to make a directory on my destination drive and copy all the documents, however, it makes the directory, and the documents subdirectory, but on my desktop, where the batch file resides, and never copies the files in Documents. The file name is also incorrect, it just assigns the time to the directory instead of the date_time.
Passing T:\Documents "T:\Backup"\"svetlana_backup_23022016_ 91300"\Documents
Making and Copying T:\Backup"\"svetlana_backup_23022016_ 91300"\Documents Directory
Making and Copying Directory
0 File(s) copied
If I were to do all of this without the label it works.

call :makedirandcopy %documents% %destinationDocuments%
goto :EOF
:makedirandcopy
ECHO Making and Copying %~2 Directory
MKDIR "%~2"
XCOPY "%~1" "%~2" /E /F
goto :EOF
From your run report, you have a line
Passing T:\Documents "T:\Backup"\"svetlana_backup_23022016_ 91300"\Documents
There is nothing in the code you've posted that will generate that line.
You then have
Making and Copying T:\Backup"\"svetlana_backup_23022016_ 91300"\Documents Directory
Making and Copying Directory
The cause of this is that you are calling :makedirandcopy (which displays the first line) and when the called routine ends (presumably it reaches end-of-file) control is returned to the statement following the call - which is the routine again, this time with no parameters, hence the second line.
Try
call :makedirandcopy "%documents:"=%" "%destinationDocuments:"=%"
goto :EOF
which will remove the excess quotes in each of the variables and quote the result.
Note that since there are spaces in the parameters being passed to :makedirandcopy, xcopy will require those parameters quoted.
Perhaps you'd also need
...
set newdate=%newdate::=%
set newdate=%newdate: =0%
...
to replace the suppressed-leading-zero in the time with a real, genuine 0.

Related

How to recursively delete all folders of a folder tree unless they contain a file with certain file extension?

How to go though a directory tree and delete all directories unless they contain a file with a particular file extension?
I tried Robocopy thinking the folders were empty. But all the folders have hidden files. So I need something that will take every folder in a directory that does not have a .pdf for example in it and delete it.
The task is to delete all directories/folders not containing a PDF file and also not containing a subdirectory/subfolder containing a PDF file. Let us look on an example to better understand the directory/folder deletion task.
The directory C:\Temp contains following subfolders and files:
Folder 1
Subfolder A
File 1A.txt
Subfolder B
File 1B.txt
Subfolder C
File 1C.pdf
File 1.cmd
Folder 2
Subfolder A
Subfolder B
File 2B.pdf
Subfolder C
File 2C.pdf
File 2.jpg
Folder 3
Subfolder A
File 3A.log
Subfolder B
File 3.doc
Last Folder & End
Subfolder A
Last File A.xls
Subfolder B
Subfolder C
Last File C.pdf
A folder is formatted bold. A hidden folder is formatted bold and italic. A hidden file is formatted italic.
The wanted folders and files after running the batch file should be:
Folder 1
Subfolder C
File 1C.pdf
File 1.cmd
Folder 2
Subfolder B
File 2B.pdf
Subfolder C
File 2C.pdf
File 2.jpg
Last Folder & End
Subfolder C
Last File C.pdf
This result can be achieved by executing following batch file:
#echo off
goto MainCode
:ProcessFolder
for /F "delims=" %%I in ('dir "%~1" /AD /B 2^>nul') do call :ProcessFolder "%~1\%%I"
if exist "%~1\*.pdf" goto :EOF
for /F "delims=" %%I in ('dir "%~1" /AD /B 2^>nul') do goto :EOF
if /I "%~1\" == "%BatchFilePath%" goto :EOF
rd /Q /S "%~1"
goto :EOF
:MainCode
setlocal EnableExtensions DisableDelayedExpansion
set "BatchFilePath=%~dp0"
if exist "C:\Temp\" cd /D "C:\Temp" & call :ProcessFolder "C:\Temp"
endlocal
Doing something recursively on a directory tree requires having a subroutine/function/procedure which calls itself recursively. In the batch file above this is ProcessFolder.
Please read the answer on Where does GOTO :EOF return to? The command goto :EOF is used here to exit the subroutine ProcessFolder and works only as wanted with enabled command extensions. FOR and CALL as used here require also enabled command extensions.
The main code of the batch file first enables explicitly the command extensions required for this batch file and disables delayed environment variable expansion to process correct also folders with an exclamation mark in name. This is the default environment on Windows, but it is better here to explicitly set this environment because the batch file contains the command RD with the options /Q /S which can be really very harmful on execution from within wrong environment or directory.
The subroutine ProcessFolder is not at end of the batch file as usual with a goto :EOF above to avoid an unwanted fall through to the command lines of the subroutine after finishing the entire task. For safety reasons the subroutine is in the middle of the batch file. So if a user tries to execute the batch file on Windows 95/98 with no support for command extensions nothing bad happens because first goto MainCode is executed successfully as expected, but SETLOCAL command line, calling the subroutine and last also ENDLOCAL fail and so no directory was deleted by this batch file designed for Windows with cmd.exe as Windows command processor instead of command.com.
The main code sets also current directory to the directory to process. So C:\Temp itself is never deleted by this code because of Windows prevents the deletion of a directory which is the current directory of any running process or contains a file opened by a running process with file access permission set to prevent other processes to delete the file while being opened by the process.
Next is called subroutine ProcessFolder with argument C:\Temp to process this folder recursively.
Last the initial environment is restored which includes also initial current directory on starting the batch file if this directory still exists.
The command for /D is usually used to do something on all subdirectories of a directory. But this is not possible here because FOR always ignores directories and files with hidden attribute set. For that reason it is necessary to use command DIR to get a list of all subdirectories in current directory including directories with hidden attribute set.
The command line dir "%~1" /AD /B 2>nul is executed by FOR in a separate command process started with cmd.exe /C in background. This is one reason why this batch file is quite slow. The other reason is calling the subroutine again and again which cause internally in cmd.exe to save and restore environment again and again.
Please read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
For each subdirectory in a directory the subroutine ProcessFolder calls itself. The first FOR loop in the subroutine is left if a directory does not contain one more subdirectory.
Then the subroutine checks in current directory if there is at least one *.pdf file. The IF condition used here is true even if the directory contains only a hidden PDF file. In this case the subroutine is exited without doing anything as this directory contains definitely a PDF file and therefore must be kept according to the requirements of the folder deletion task.
Next is checked if the current directory still contains at least one subdirectory as in this case the current directory must be also kept as one of its subdirectories contains at least one PDF file.
Last the subroutine checks if the current directory contains by chance the batch file as this directory must be also kept to finish the processing of the batch file.
Otherwise the current directory is deleted with all files on not containing a PDF file and no subdirectories and also not the currently running batch file as long as Windows does not prevent the deletion of the directory because of missing permissions or a sharing access violation.
Please note that the batch file does not delete other files in a directory which is not deleted as it can be seen also on the example.
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 /?
cd /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
rd /?
setlocal /?

Run Batch Script Across Subfolders (Recursively)

I regularly have to rename hundreds of files across a subfolder structure. I have been creating a batch file consisting of all my rename commands, and manually pasting this into each subfolder to execute one subfolder at a time. I'd like to revise the batch script so that it executes against all subfolders in one fell swoop, run from the parent directory just once.
My renaming is very manual, and so I need to create a discrete entry for each file. For example, here are three lines:
REN STWP01_00669087* BCBSRI-01849351*
REN BCBSRI-01849357* 2011-12-19_BCBSRI-01849357*
REN STWP01_00669094* BCBSRI-01849369*
I've experimented with the FOR /R command, including trying a separate batch file that calls my renaming batch file (via the CALL command). No luck.
I have to assume that this is simple, but I'm a batch novice, so any help would be appreciated.
Thanks!
#Magoo,
Thanks so much for your response. Your approach is going to be far more efficient than my own so far.
A couple of questions. Please bear with me as I am a total novice with batch commands.
Here's what I did: I saved your code to a .BAT file ("RRename.bat"), modified my filenames as per your instructions and saved those to a text file ("Filenames.txt"), and then run this command from the command line: {RRename.bat Filenames.txt}.
The resulting command windows confirm correct renaming. And so I removed the ECHO and PAUSE commands and re-ran. No luck. Just a bunch of Command windows confirming the directory.
Ideally I'd love to save this as a .BAT file and simply drop this in the top-level directory, together with the data file that contains the old names and new names of the files. And so, a double-click of "RRename.bat" will parse the content of "Filenames.txt" and work its way through all subfolders, renaming wherever matches are encountered. Boom.
To that end:
1. How do I make it so {SET "sourcedir=} indicates the current directory (i.e. the directory in which the batch file is located)? This way I wouldn't ever need to change this variable. (I should note that I am running this script on a network location, which requires me to map the drive, resulting in a different drive letter every time.)
2. How do I hard-code the name of the data file into the script itself? My goal is an easily replicated process minimizing user input (save for the content of the data file).
3. How do I stop the individual command windows from appearing? I'll be renaming thousands of files at a time and don't want to see thousands fo corresponding command windows.
Thank you!
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: read parameters
SET "filename1=%~1"
SET "filename2=%~2"
IF DEFINED filename2 GOTO name
IF NOT DEFINED filename1 GOTO :EOF
:: 1 parameter - must be filename
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
GOTO :eof
:: we have 2 parameters so rename pattern 1 to pattern 2
:name
FOR /r "%sourcedir%" %%a IN ("%filename1%*") DO CALL :process "%%a"
PAUSE
GOTO :EOF
:: Process the filenames and actually do the rename
:process
:: Name of file to be changed - name and extension of %1
SET "changeme=%~nx1"
:: REPLACE up-to-from-pattern with nothing = remainder of name/extension
CALL SET "endpart=%%changeme:*%filename1%=%%"
:: and RENAME...
ECHO(REN "%~1" "%filename2%%endpart%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
Revised data file
STWP01_00669087 BCBSRI-01849351
BCBSRI-01849357 2011-12-19_BCBSRI-01849357
STWP01_00669094 BCBSRI-01849369
Aimed at processing the above file, renaming files starting (column1 entries) to start (column2 entries.)
Method:
Run the batch as
batchname filename
This will execute the batch, processing filename
How:
having set the directory name to start processing from, set filename1&2 to the values of the parameters supplied.
If only 1 is supplied, it is the filename, so process it line-by-line and START a new process /min minimised "with the window name in the first set of quotes" and execute this same batch with the data from each line of the file in turn, then finish by going to :eof (end-of-file - built-in to CMD)
The sub-processes all have 2 parameters (eg BCBSRI-01849357 2011-12-19_BCBSRI-01849357) so processing passes to :name. This runs a for /r loop, from the specified source directory, with the name specified from the first column+* and executes :process passing the filenames found as parameter 1.
:process sets changeme to the filename in question, calculates endpart by removing the string filename1 from changeme which will deliver the er, end part.
Then simply rename the supplied filename to the replacement name+that endpart calculated.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
The PAUSE is just to allow the proposed changes to be seen. Once the process has been verified, change the PAUSE to EXIT.
AAMOI, running
*batchname* STWP01_00669094 BCBSRI-01849369
for instance, would execute the recursive-rename from STWP01_00669094* to BCBSRI-01849369*
Sadly, "No luck" is meaningless.
I have made a minor, but significant change to the instructions. The PAUSE should be changed to an EXIT after testing.
After testing, the ECHO(... line should become
REN "%~1" "%filename2%%endpart%"
which actually executes the rename. If you've just deleted the line, it would explain the no-visible-result.
Having restored the original code and verified against a small representative dummy subtree, change the echo(... line and test again. The filenames should change. If not, something is dreadfully wrong. Needless to say, this works perfectly happily for me...
Then try again with the PAUSE changed to EXIT. This time, the windows generated will appear on the taskbar and then disappear when the rename for that line of the input file has finished. This will happen once for BCBSRI-01849357 rentwo for instance - not once for each individual file rename occurring.
To hard-code the filename, remove the line
IF NOT DEFINED filename1 GOTO :EOF
and replace
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
with
FOR /f "usebackqdelims=" %%a IN ("YOURFILENAMEHERE") DO START /min "ren %%a" "%~f0" %%a
For the "run from here" command, change
SET "sourcedir=U:\sourcedir"
to
SET "sourcedir=."
. means "the current directory"
If you place thisbatchfilename.bat into any directory on your PATH then you can run the routine simply by executing thisbatchfilename.
You can display your path by typing
path
at the prompt. PATH is the sequence of directories searched by windows to find an executable if it isn't found in the current directory. To chane path, google "change path windows" - experienced batchers create a separate directory on the path for batch files. Sometimes, they name the directory "Belfry".

copy paste files using CMD

I want to accomplish the following: copy File A from Directory A to Directory B but File A already exist in Directory B and i don't want to overwrite it, simply put the new file in Directory B with a (1) or something so i'll know the difference. i can't manually name the file in the script as i plan to make it a batch file and run it every few hours therefore resulting in the same file existing with different names. IE. File A, File A(1), File A(2) so on and so on. please help. Thank you
Use Xcopy command with parameter /D
C:\> xcopy D:\source_dest E:\target_dest /E /D
/E parameter make that it copy files with subfolders
/D parameter make that it copy files without overwrite
if want more help inform me .if it works vote up
A cyclic copy refers to the situation where the source directory
contains the destination directory. Since the destination directory
is part of the source, the copying process eventually starts copying
the destination directory to a deeper portion of the destination.
This process will continue until, of course, the finite disk space
eventually runs out.
To avoid this condition but to achieve the objective, one can
specify a temporary destination which is on another volume (e.g.,
D:\temp\) and later copy the temporary destination to the final
destination, and delete the temporary directory at the end.
just check file exist before run xcopy command then run copy command
if NOT EXIST c:\directory B\File A ( xcopy c:\Directory A\File A c:\Directory B )
The following is made for folders but you can modify it for files.
Makes a backup of existed folder directory (and all contents in it) with new increase number in folder name [like TEST(1)... TEST(2)... folder].
The first part :Start will set the %PathFolder% variable with path to folder name (%userprofile%\Desktop\TEST) and then will search if exist. If NOT exists, will create it (xcopy), else, If exist, will directing the %PathFolder% variable to :Search label (for-)loop section for further handling...
Tip: Can be used %1 variable to set PathFolder set "PathFolder=%1" so it will work when you Drag-n-Drop a folder on this current saved batch file.
The second part :Search will search in the %PathFolder% variable (%userprofile%\Desktop\TEST) and will make a COPY of "TEST" folder (and all contents in it) with an increase number added in parenthesis at the end of folder name [like TEST(1)]. If already exist TEST(1) then will copy TEST folder as TEST(2) ... or TEST(3) ... and so on.
::Make a backup of existed folder directory (and all contents in it) with new increase number in folder name.
#echo off
setlocal EnableDelayedExpansion
set "IncrNum=1"
:Start
::The following will set the "%PathFolder%" variable with path to folder name (%userprofile%\Desktop\TEST) and then will search if exist. If NOT exists, will create it (xcopy), else, If exist, will directing the "%PathFolder%" variable to :Search label (for-)loop section for further handling...
::Tip: Can be used %1 instead of %userprofile%\Desktop\TEST in [set "PathFolder=..."] line so it will work when you Drag-n-Drop a folder on this current saved batch file.
set "PathFolder=%userprofile%\Desktop\TEST"
if NOT exist "%PathFolder%" (
xcopy %PathFolder% %PathFolder% /i /y
exit /b
) else (
goto :Search
)
exit /b
:Search
::The following will search in the "%PathFolder%" variable (%userprofile%\Desktop\TEST) and will make a COPY of "TEST" folder (and all contents in it) with an increase number added in parenthesis at the end of folder name [like TEST(1)]. If alredy exist TEST(1) then will copy TEST folder as TEST(2) ... or TEST(3) ... and so on.
for /f "tokens=*" %%G in ('dir /b /s /a:d "%PathFolder%*"') do (
if exist %%G^(%IncrNum%^) (
echo At "%%~dpG" a folder "%%~nG(%IncrNum%)" alredy existed.
set /a IncrNum+=1
goto :Search
) else (
echo.
echo.
echo The "%%~nG" folder and all contents in it
echo will be copied now as "%%G(%IncrNum%)".
echo.
pause
xcopy %%G %%G^(%IncrNum%^) /i /y
exit /b
)
)
:EndBatch
set "PathFolder="
pause
exit

How to create flat directory from folder hierarchy and handle duplicate files?

I want to parse a directory and its subdirectories and copy all files in a target directory ignoring the original folder structure. All files shall be copied directly in the target directory.
I found a nice solution here for the Windows command line:
for /r FolderA %f in (*) do #copy "%f" target
https://stackoverflow.com/a/1502183/772434
That in general works fine, but I have duplicate files in the source directory which have the same name. There are too many files, so I can not handle those exceptions by hand.
How can I handle those files in the script automatically?
Options:
overwrite files during copying
keep copies and rename file e. g. by adding "__123" a number at the end of the file name
compare files (MDS5 sum or similar) and create copy only if files are different.
I would prefer option 3 (compare file content and keep numbered copies only if files are really different), but I'm also interested in solutions for options 1 and 2 (more pragmatic).
Here's an approach. i haven't tested it, so i hope it works as expected.
There is a global variable called CURRENT_DUPLICATE_SUFFIX. every time a file with the same name is found, but with different content, this value gets incremented and appended to the resulting file name
#echo off
SET SOURCE_DIR=C:\temp\LogAnalyzer
SET TARGET_DIR=C:\temp\test\output
SET CURRENT_DUPLICATE_SUFFIX=1
for /r "%SOURCE_DIR%" %%f in (*) do CALL :SUB1 "%%f"
GOTO END
:SUB1
SET PURE_FILE_NAME=%~nx1
SET TARGET_FILE_NAME=%TARGET_DIR%\%PURE_FILE_NAME%
IF NOT EXIST "%TARGET_FILE_NAME%" (
COPY %1 "%TARGET_DIR%"
GOTO :EOF
)
REM if we are here, the target file exists.
echo n |comp %1 "%TARGET_FILE_NAME%" >NUL 2>NUL
REM in case the files have the same content
IF %ERRORLEVEL%==0 GOTO :EOF
SET TARGET_FILE_NAME=%TARGET_FILE_NAME%_%CURRENT_DUPLICATE_SUFFIX%
SET /A CURRENT_DUPLICATE_SUFFIX=%CURRENT_DUPLICATE_SUFFIX%+1
COPY %1 "%TARGET_FILE_NAME%
GOTO :EOF
:END

BATCH FILE - IF Exists & Output Error

INFO: assets.txt contains a list of cpu names which I can connect to over the network.
I need to copy this new .exe to well over 200+ computers and figured i could use the c$ admin share. This is really the only way I can do this without going to workstations individually or remoting in one by one.
This script works without the 'if exists' however I need to check if the directory exists before attempting the copy. I don't understand why it isn't working. I am also running this script using my domain administrative account.
#echo off
REM Pull Computer Asset Tags from file
for /F "tokens=*" %%A in (assets.txt) do (
echo Start Processing %%A
REM Temporarily set file path for existence check
set file=\\%%A\C$\Program Files\Intouch2\Intouch2ca.exe
if EXIST "%file%" (
REM Rename old .exe
ren "\\%%A\C$\Program Files\Intouch2\Intouch2ca.exe" "Intouch2ca.bak"
REM copy new .exe from server to cpu asset
xcopy "\\server\my dir\management\it\software\Intouch Upgrade\Intouch2ca.exe" "\\%%A\C$\Program Files\Intouch2\" /Y
echo END Processing %%A
echo.
echo ------------------------------------------------------------
echo.
)
)
I also haven't been able to get the error output to a log file.
I have tried this but it isnt exactly what I would like.
xcopy "\\server\my dir\management\it\software\Intouch Upgrade\Intouch2ca.exe" "\\%%A\C$\Program Files\Intouch2\" /Y 1>>errors.log 2>&1
How can I pretty that up so it only shows errors and lists the %%A where the error occured?
Thank you all in advance for your time.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed.
Hence, in your case, file is being changed within the block, so the value cmd uses is its initial value when the entire for is parsed.
Solution 1: use \\%%A\C$\Program Files\Intouch2\Intouch2ca.exe in place of %file%
Solution 2: start your batch with setlocal enabledelayedexpansion on a separate line after the #echo off, then use !file! in place of %var%
Solution 3: call an internal routine to use the mofified value as %file%
Solution 4: Create the directory regardless. MD newname 2>nul will silently create a new directory if it doesn't already exist
An error on a copy will set an errorlevel and you can write a custom error message.
copy "\\server\my dir\management\it\software\Intouch Upgrade\Intouch2ca.exe" "\\%%A\C$\Program Files\Intouch2\" >nul 2>&1
if errorlevel 1 >> errors.txt echo "Error in %%A"

Resources