Command prompt logging with xcopy - windows

I have an xcopy script that I'm running that reads in a .csv file with directories and file names and parses it to copy the files.
Here's the script:
echo F | for /f "delims=, tokens=1,2,3" %i in (D:\foo.csv)
do xcopy /i /d "Z:\%i\%j\%k" "Y:\%i\%j\%k" >> "D:\xcopy\Log.txt"
The output to the command prompt are the commands being executed:
echo F | xcopy /i /d "Z:\hcri001\a1\
ffce5a14-33ca-43cf-b366-af266c450979" "Y:\hcri001\a1\ffce5a14-33ca-43cf-b366
-af266c450979" 1>>"D:\xopy\Log.txt
However, the log just has the output of the commands:
0 File(s) copied
0 File(s) copied
0 File(s) copied
Does Y:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3 specify a file name
or directory name on the target
(F = file, D = directory)? F
Z:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3
1 File(s) copied
Is there someway to have both the commands being executed, as well as the output?
Also, is there anyway to have some sort of counter that I can put in the beginning of the line?
My Ideal log file would be in this format:
1) C:\Desktop>echo F | xcopy /i /d "Z:\hcri001\a7\
00a62a73-d7a7-4cfb-b55c-457bc67b3647" "Y:\hcri001\a7\00a62a73-d7a7-4cfb-b55c
-457bc67b3647" 1>>"D:\Robocopy\AtlusPatient131172CopyLog.txt
0 File(s) copied
2) C:\Desktop>echo F | xcopy /i /d "Z:\hcri001\d5\
003452354-d7a7-4cfb-452c-457bc67b3647" "Y:\hcri001\d5\003452354-d7a7-4cfb-452c-457bc67b3647" 1>>"D:\Robocopy\AtlusPatient131172CopyLog.txt
0 File(s) copied
3) C:\Desktop>echo F | xcopy /i /d "Z:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3" "Y:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3" 1>>"D:\Robocopy\AtlusPatient131172CopyLog.txt
Does Y:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3 specify a file name
or directory name on the target
(F = file, D = directory)? F
Z:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3
1 File(s) copied
and bonus points if the command can be trimmed to just include the file name instead of the whole command i.e. 1) "Z:\hcri001\2d\545392db-50fa-40d9-aaa2-0892ca5057f3"
0 File(s) copied

for /f "delims=, tokens=1,2,3" %i in (D:\foo.csv) do (
echo xcopy /i /d "Z:\%i\%j\%k" "Y:\%i\%j\%k" >> "D:\xcopy\Log.txt"
echo f | xcopy /i /d "Z:\%i\%j\%k" "Y:\%i\%j\%k" >> "D:\xcopy\Log.txt"
)
For the counter you need SETLOCAL ENABLEEXTENSIONS then set counter=1
and inside the loop set /a counter=!counter! + 1

Related

Batch list folder, count pdf files and get file name

I would like to create a summary of all the files that I received weekly from other department. The folder structure shown below. Each folder will contain multiples pdf files and only 1 .docx file. So, i would like to list down the folder name, count total pdf files inside and get the name of docx file. If docx missing, show "missing", is this possible too?
I did try this code, but this will show all files in the folder instead.
#echo off
FOR /F "delims=" %%F IN ('dir /B /A /S *') DO (
for %%D in ("%%~dpF\.") do echo/ FolderName: %%~nxD ^| FileName: %%~nxF
)
pause
UPDATE:Today while Im testing suggested code, they send me last week file. AND to my surprised, the other department changed folder structure (facepalm!). Below updated folder structure for reference.
Updated folder structure:
Current
|count.bat
|--FolderA-Folder1-Nov 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
| Folder2-Dec 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
|--FolderB-Folder1-Nov 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
| Folder2-Dec 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
|--FolderC-Folder1-Nov 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
Expectation Results
FolderName: Folder A Folder 1 PDFtotal: 4pdfs DocFile: Nov 2020.docx
FolderName: Folder A Folder 2 PDFtotal: 4pdfs DocFile: Dec 2020.docx
FolderName: Folder B Folder 1 PDFtotal: 4pdfs DocFile: Nov 2020.docx
FolderName: Folder B Folder 2 PDFtotal: 4pdfs DocFile: Dec 2020.docx
FolderName: Folder C Folder 1 PDFtotal: 4pdfs DocFile: Nov 2020.docx
Original Script
The dir command without the /B option displays the count of matching items in the penultimate line, which could be captured and parsed by a for /F loop.
Since you have got a flat directory structure, I would not do a recursive approach (like dir /S or for /R).
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (target directory; `.` is current, `%~dp0.` is parent)
set "_MASK=Doc*.docx" & rem // (pattern to find particular file)
set _LIST="pdf" & rem // (space-separated list of file extensions without `.`)
rem // Loop through immediate sub-directories of the target directory:
for /D %%D in ("%_ROOT%\*") do (
rem // Return name of current sub-directory without trailing line-break:
< nul set /P ="FolderName: %%~nxD "
rem // Loop through given list of file extensions:
for %%E in (%_LIST%) do (
rem // Initialise variables:
set /A "CNT=0, NUM=0" & set "NAME="
rem // Capture number of matching files from last but one line of `dir`:
for /F "eol= " %%C in ('2^> nul dir /A:-D-H-S "%%~D\*.%%~E"') do (
2> nul set /A "CNT=NUM, NUM=%%C"
)
rem // Return count of matching files without trailing line-break:
< nul call set /P ="PDFtotal: %%CNT%%%%~Es "
)
rem // Find file that matches the given pattern (assuming there is only one):
for %%F in ("%%~D\%_MASK%") do set "NAME=%%~nxF"
rem // Return name of found file, with trailing line-break this time:
call echo DocFile: %%NAME%%
)
endlocal
exit /B
The option /A:-D-H-S of the dir command in the code prevents hidden and system files from being counted; change it to /A:-D if you do want to regard them as well.
Updated Script
In the following you will find the new code adapted to your changed directory structure (you will notice that there were only a few modifications necessary):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (target directory; `.` is current, `%~dp0.` is parent)
set "_MASK=* 2020.docx" & rem // (pattern to find particular file)
set _LIST="pdf" & rem // (space-separated list of file extensions without `.`)
rem // Loop through immediate sub-directories of the target directory:
for /D %%S in ("%_ROOT%\*") do (
rem // Loop through next level of sub-directories:
for /D %%D in ("%%~S\*") do (
rem // Return names of current sub-directories without trailing line-break:
< nul set /P ="FolderName: %%~nxS %%~nxD "
rem // Loop through given list of file extensions:
for %%E in (%_LIST%) do (
rem // Initialise variables:
set /A "CNT=0, NUM=0" & set "NAME="
rem // Capture number of matching files from last but one line of `dir`:
for /F "eol= " %%C in ('2^> nul dir /A:-D-H-S "%%~D\*.%%~E"') do (
2> nul set /A "CNT=NUM, NUM=%%C"
)
rem // Return count of matching files without trailing line-break:
< nul call set /P ="PDFtotal: %%CNT%%%%~Es "
)
rem // Find file that matches the given pattern (assuming there is only one):
for %%F in ("%%~D\%_MASK%") do set "NAME=%%~nxF"
rem // Return name of found file, with trailing line-break this time:
call echo DocFile: %%NAME%%
)
)
endlocal
exit /B
#echo off
setlocal enabledelayedexpansion
set "spaces= "
FOR /F "delims=" %%F IN ('dir /B /AD /S *') DO (
rem new foldername in %%F
set "foldername=%%~nxF%spaces%"
set /a pdfcount=0
set /a docxcount=0
set "docxmessage=missing"
for /f "delims=" %%D in ('dir /B /A-D "%%F\*"') do (
if /i "%%~xD"==".pdf" set /a pdfcount+=1
if /i "%%~xD"==".docx" set /a docxcount+=1&set "docxmessage=%%~nxD"
)
set "pdfcount=%spaces%!pdfcount!"
if !docxcount! gtr 1 (set "docxcount= MULTIPLE") else (set "docxcount=")
echo/ FolderName: !foldername:~0,20! PDFtotal: !pdfcount:~-3!pdfs Docfile: !docxmessage! !docxcount!
)
Note that the first dir command now contains AD to list directories only.
With each directoryname, record it in foldername and add some spaces at the end. Reset the counters and establish the missing value in the docxmessage.
Then get the filename list within the directory found, count each .pdf and .docx and if any .docx is found, record its name in docxmessage
Modify docxcount to a message if more than 1 .docx is present, and empty otherwise
When the directory-scan is finished, apply some spaces to the start of pdfcount and then produce your output line using the current values of the variables and using the modifiers to produce the first 20 characters of the padded directoryname and the last 3 characters of the padded pdfcount.
When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.
Fixed : for /F %%D... needed "delims=" to assign the whole filename to %%D
Revised code for new structure:
#echo OFF
setlocal ENABLEDELAYEDEXPANSION
PUSHD U:\sourcedir
set "spaces= "
FOR /F "delims=" %%F IN ('dir /B /AD /S *') DO (
rem new foldername in %%F
set "foldername=%%~nxF%spaces%"
rem find parent directory
FOR /F "delims=" %%Q IN ("%%~dpF\.") DO SET "parent=%%~nxQ%spaces%"
rem is a leaf directory if this directory has no subdirectories
SET "isleaf=Y"
FOR /F "delims=" %%Q IN ('dir /B /AD "%%F"') DO SET "isleaf="
set /a pdfcount=0
set /a docxcount=0
set "docxmessage=missing"
for /f "delims=" %%D in ('dir /B /A-D "%%F\*" 2^>nul') do (
if /i "%%~xD"==".pdf" set /a pdfcount+=1
if /i "%%~xD"==".docx" set /a docxcount+=1&set "docxmessage=%%~nxD"
)
set "pdfcount=%spaces%!pdfcount!"
if !docxcount! gtr 1 (set "docxcount= MULTIPLE") else (set "docxcount=")
IF DEFINED isleaf echo/ FolderName: !parent:~0,20! !foldername:~0,20! PDFtotal: !pdfcount:~-3!pdfs Docfile: !docxmessage! !docxcount!
)
popd
GOTO :EOF
Some small additions to the code.
first, find the parent directoryname. By using the directoryname in %%F and adding \., the resultant resolved string appears to be a filename, the name and extension of which is the parent directory; store this in parent with padding.
Next, determine whether this is a leaf directory. Set the flag isleaf to assume it is a leaf directory, then perform a dir/ad so that any detected subdirectory name will clear the flag.
Then just process the directory as before, using 2^>nul to suppress file not found messages if the directory %%F contains no files.
...And only generate the report line if isleaf is defined, with the parent directoryname added.
This can be placed into a .bat file script and run in cmd.exe. Note that this does not identify directories which contain no .pdf file, no .docx file, or multiple .docx files.
powershell.exe -NoLogo -NoProfile -Command ^
"$Dirs = Get-ChildItem -Directory -Path 'C:\src\t';" ^
"foreach ($Dir in $Dirs) {" ^
" $PdfCount = (Get-ChildItem -File -Path $Dir.fullname -Filter '*.pdf').Count;" ^
" if (($null -ne $PdfCount) -and (0 -ne $PdfCount)) {" ^
" $DocxFiles = (Get-ChildItem -File -Path $Dir -Filter '*.docx');" ^
" if ($null -ne $DocxFiles) { $DocxFile = $DocxFiles[0] } else { $DocxFile = 'none' };" ^
" """FolderName: $($Dir.Name) PDFtotal: $($PdfCount.ToString())pdfs DocFile: $DocxFile""";" ^
" }" ^
"}"
It would be easier to put the script into a file.
=== Get-DocxAndPdfCount.ps1
[CmdletBinding()]
Param()
$Dirs = Get-ChildItem -Directory
foreach ($Dir in $Dirs) {
Write-Verbose "dir is $($Dir.fullname)"
$PdfCount = (Get-ChildItem -File -Path $Dir.fullname -Filter '*.pdf').Count
Write-Verbose $PdfCount
if ($null -ne $PdfCount -and 0 -ne $pdfCount) {
Write-Verbose $PdfCount
$DocxFiles = (Get-ChildItem -File -Path $Dir -Filter '*.docx')
if ($null -ne $DocxFiles) { $DocxFile = $DocxFiles[0] } else { $DocxFile = 'none' }
"FolderName: $($Dir.Name) PDFtotal: $($PdfCount.ToString())pdfs DocFile: $DocxFile"
}
}
Then, run it in a PowerShell console using:
.\Get-DocxAndPdfCount
.\Get-DocxAndPdfCount -Verbose # to see debugging information
Run from cmd.exe using:
powershell -NoLogo -NoProfile -File '.\Get-DocxAndPdfCount'
powershell -NoLogo -NoProfile -File ".\Get-DocxAndPdfCount" -Verbose

How to remove a varying string from a set of other, longer strings?

Background information: Directory c:\documents is full of .doc and .xls files from different people. There are put in initials somewhere in the filename in order to identify who edited the file. Every filename may have one or more of initial sets. I am only interested in .doc files this time. A cross section of this directory is looking like this:
depot.inventory.20180921.[CMP]-[OxA](DOT)-(TTR).edited.doc
rack_location_(IIY)collected.2018.11.24.edit[UTS]_{POM}.doc
The list goes on and on for hundreds of files. I want to generate a copy of these files without the editors´ initials and put them into a directory called c:\uniform.
The constants here are: every set of initials are 3 letters long and can be upper or lower case and enclosed in some sort of brackets. At any given time, I have a list of editors´ initials in a file one set per line format, such as:
CMP
OXA
TTR
DOT
UTS
IIY
POM
The file has about 100-150 names on any given day.
I figured out so far how to remove one set of initials from all .doc files as follows:
for /R "C:\documents" %%f in (*.doc) do (
call :Sub %%~nf
)
:Sub
set str=%*
set str=%str:[DOT]=%
echo %str%
Here, in this code segment, I put [DOT] as an example. I want to make the string [DOT] a variable and read it from the editors´ initials file. However, this is necessary for each document file many times.
So my batch program is going to loop thru all *.doc files in source directory, for each file, it will go thru a loop of 100-150 names and remove those strings and form a new filename and copy the old file from source directory, into the destination directory, with the new filename, which is the editors´ initials stripped off version of the source filename.
How can I do the second loop?
I am stumped at the syntax.
Here is a commented batch file for this unusual file copying task.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=C:\documents"
set "DestinationFolder=C:\uniform"
rem Is there no *.doc file to process in source directory?
if not exist "%SourceFolder%\*.doc" goto :EOF
rem Do nothing if the text file with editors' initials
rem does not exist in the batch file directory.
if not exist "%~dp0EditorsInitials.txt" goto :EOF
rem Create the destination directory on not already existing
rem and veriy the real existence of the destination directory.
md "%DestinationFolder%" 2>nul
if not exist "%DestinationFolder%\" goto :EOF
rem Read the editors' initials from text file and create a space separated
rem list of them assigned to the environment variable EditorsInitials.
setlocal EnableDelayedExpansion
set "EditorsInitials="
for /F "usebackq" %%I in ("%~dp0EditorsInitials.txt") do set "EditorsInitials=!EditorsInitials! %%~I"
endlocal & set "EditorsInitials=%EditorsInitials:~1%"
rem For each non-hidden *.doc file in source directory get file name with
rem file extension and with path if there is one specified left to *.doc
rem and assign it to the environment variable FullFileName. The file name
rem only is assigned to the environment variable FileName. Then delayed
rem environment variable expansion is enabled again for running two nested
rem loops which runs case-insensitive string substitutions on the file name
rem string value to remove the editors' initials from the file name. Next
rem one more loop is used to remove also .edited and .edit from file name.
rem The current *.doc file is finally copied with cleaned file name to
rem the configured destination directory. A date in file name remains.
for %%I in ("%SourceFolder%\*.doc") do (
set "FullFileName=%%I"
set "FileName=%%~nI"
setlocal EnableDelayedExpansion
for %%J in (%EditorsInitials%) do for %%K in ("-" "." "_" "") do (
set "FileName=!FileName:%%~K[%%J]=!"
set "FileName=!FileName:%%~K(%%J)=!"
set "FileName=!FileName:%%~K{%%J}=!"
)
for %%J in (".edited" ".edit") do set "FileName=!FileName:%%~J=!"
copy "!FullFileName!" "%DestinationFolder%\!FileName!%%~xI" >nul
endlocal
)
endlocal
This batch file executes the command copy with the file EditorsInitials.txt in directory of the batch file containing posted list of editors' initials for the two example *.doc files with the arguments:
"C:\documents\depot.inventory.20180921.[CMP]-[OxA](DOT)-(TTR).edited.doc" "C:\uniform\depot.inventory.20180921.doc"
"C:\documents\rack_location_(IIY)collected.2018.11.24.edit[UTS]_{POM}.doc" "C:\uniform\rack_locationcollected.2018.11.24.doc"
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 /? ... explains %~dp0 ... drive and path of argument 0 which is the full path of the directory containing this batch file always ending with a backslash.
copy /?
echo /?
endlocal /?
for /?
goto /?
if /?
md /?
rem /?
set /?
setlocal /?
See also the Microsoft article about Using command redirection operators for an explanation of >nul and 2>nul.
This solution uses a regex in PowerShell. If you are on a supported Windows system, it will have PowerShell. This does assume that no one uses a VERTICAL LINE as part of their initials nor as a bracket around initials. Change the $DestinationDir to your choice.
When you are confident that the files will be renamed correctly, remove the -WhatIf from the Rename-Item command.
=== Rename-Initials.ps1
$SourceDir = 'C:\src\t\reninitials'
$DestinationDir = 'C:\src\t\reninitials\uniform'
$Editors = (Get-Content -Path $(Join-Path -Path $SourceDir -ChildPath 'Editors.txt')) -join '|'
$OpeningBrackets = #('\[', '\(', '{') -join '|'
$ClosingBrackets = #('\]', '\)', '}') -join '|'
$Regex = '(' + $OpeningBrackets + ')(' + $Editors + ')(' + $ClosingBrackets + ')'
$FileTypes = #('*.doc', '*.xls')
foreach ($FileType in $FileTypes) {
Get-ChildItem -Path $SourceDir -File -Recurse -Filter $FileType |
ForEach-Object {
if ($_.Name -match $Regex) {
$NewName = $_.Name -replace $Regex,''
Move-Item -LiteralPath $_.FullName `
-Destination $(Join-Path -Path $DestinationDir -ChildPath $NewName) -WhatIf
}
}
}
If you must call it from a cmd.exe shell:
powershell -NoLogo -NoProfile -File "Rename-Initials.ps1"
The task you are trying to accomplish is not that trivial, particularly when you do not want to leave sequences of separators, like periods, hyphens, underscores, etc., after removal of the parenthesised string portions.
Here is a script that removes the parenthesised known editors' initials (predefined in a list file initials.txt in the current directory) one after another; if two adjacent separators (like ., -, _, and also ,, ;, %) would be left behind, the first one is removed; if no such would be there, the one defined first (.) is inserted. Optionally, a potential tail consisting of a known suffix (like edited or edit as defined in the script) and a preceding separator becomes removed too. So this is the code, including some explanatory rem remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=C:\documents" & rem // (root directory; `.` is current, `%~dp0.` is script's parent)
set "_DEST=C:\uniform" & rem // (destination directory)
set "_OVER=" & rem // (set this to `|` to overwrite existing files, or else to ``)
set "_LIST=initials.txt" & rem // (text file containing list of editors' initials, one per line)
set _MASKS="*.doc" "*.xls" & rem // (list of file patterns to process)
(set _LF=^
%= blank line =%
) & rem // (line-break)
set _PAREN=( )^%_LF%%_LF%[ ]^%_LF%%_LF%{ } & rem // (list of pairs of parentheses)
set _SEPAR=. - _ "," ";" %% & rem // (list of separators; do not use `=`, `~`, `!`, `^`)
set _TAILS="edited" "edit" & rem // (optional list of suffixes to remove; may be empty)
rem // Change into root (source) directory:
pushd "%_ROOT%" && (
rem // Iterate through all matching files:
for %%F in (%_MASKS%) do (
rem // Store full name of current file:
set "FILE=%%~F" & set "NAME=%%~nxF"
rem // Toggle delayed expansion to avoid trouble with `!`:
setlocal EnableDelayedExpansion
rem // Loop over the list of initials:
for /F "usebackq delims= eol=|" %%E in ("%_LIST%") do (
rem // Loop over trailing separators:
for %%J in (. !_SEPAR! "") do (
rem // Loop over leading separators:
for %%I in (!_SEPAR! "") do (
rem // Loop over pairs of parentheses:
for /F "tokens=1,2" %%K in ("!_PAREN!") do (
rem // Conditionally remove parenthesised text from file name:
if not "%%~J"=="" (
set "NAME=!NAME:%%~I%%K%%E%%L%%~J=%%~J!"
) else if not "%%~I"=="" (
set "NAME=!NAME:%%~I%%K%%E%%L%%~J=%%~I!"
) else if defined _SEPAR (
set "NAME=!NAME:%%~I%%K%%E%%L%%~J=%_SEPAR:~,1%!"
) else (
set "NAME=!NAME:%%~I%%K%%E%%L%%~J=.!"
)
)
)
)
)
rem // Process optional list of suffixes:
if defined _TAILS (
rem // Use `for /F` loop to split file name into base name and extension:
for /F "delims= eol=|" %%N in (""!NAME!"") do (
endlocal
rem // Store file name components:
set "NAME=%%~nxN" & set "EXT=%%~xN" & set "TEST=%%~nN|"
setlocal EnableDelayedExpansion
rem // Loop over suffixes:
for %%M in (!_TAILS!) do (
rem // Loop over separators:
for %%J in (!_SEPAR!) do (
rem // Remove found suffix from base name:
if not "!TEST!"=="!TEST:%%~J%%~M|=!" (
set "NAME=!TEST:%%~J%%~M|=!!EXT!"
)
)
)
)
)
rem // Actually copy file to destination with the newly built name:
if not exist "!_DEST!\!NAME!!_OVER!" (
ECHO copy /Y "!FILE!" "!_DEST!\!NAME!"
)
endlocal
)
popd
)
endlocal
exit /B
Configure the exact behaviour in the section Define constants here: at the top.
After having tested the output, remove the upper-case ECHO command to actually copy files; to suppress numerous lines 1 file(s) copied. returned by the copy command, replace that ECHO by > nul instead.

Compare file line count with a file content in all sub directories using batch file

I can compare the count of lines in a file with content of another file
for example:
file1 line count is 10
file2 contains 12
then not equal
using the following script
#ECHO off
rem using linecount and putfileinvar to compare count and datafile
Set _File=testfile.txt
Set /a _Lines=0
For /f %%j in ('Type %_File%^|Find "" /v /c') Do Set /a _Lines=%%j
set /p myvar= < filecount.tmp
rem echo %myvar%%_lines%
IF %myvar% == %_lines% (ECHO eq ) ELSE (ECHO neq )
Now I want do do this for a whole directory and its sub directories where every sub directory contains two files
datafile and its count in a sperate file
I want to compare the number of lines of the datafile with the count file
:: list foleders in the root directory and store them in list file
dir /b /o:n /ad > dirlist.txt
:: for each directory in list
FOR /F "tokens=*" %%v in (dirlist.txt) do (
::store dat file names in each directory in a temp file
::Log inside each folder
cd %%v
::list dat files in file.txt
dir *.dat /b /o:n > file.txt
::for each file stored in file.txt
FOR /F "tokens=*" %%m in (file.txt) do (
::count lines inside dat file
findstr /R /N "^" %%m | find /C ":" > save_temp_count.txt
::Get the contents of the temp file and save it to the %mycount% variable
set /a mycount<=save_temp_count.txt
)
::delete temp files
del file.txt
::Get all files tmp files
dir *.tmp /b /o:n > file.txt
::get file names in file.txt
for /F "tokens=*" %%j in (file.txt) do (
set /a fcount=<%%j
)
if %fcount% == %mycount% (echo 1eq ) else (echo neq & echo %%v >> ../result.txt)
::delete temp files
del file.txt
del save_temp_count.txt
::go up back to parent folder
cd ..
)
copy this and paste it in the root directory where your folders exist , note that this script will not handle unexpected scenarioes , like:
found more than 2 files in a folder.
extensions are different from expected ( dat , tmp ).
tmp files dont contain an integer.
hope this helps.

Copy Subdirectories, Keep the Larger File

I'm trying to combine two directories on a Windows 7 machine that have some of the same subfolders (but not all) and some of the same files (but not all). I'd like to copy
For example, I have the following directory structure and want to combine (copy or move) "Dir_A" and "Dir_Z". Where the file exists in the destination directory, I want to compare filesizes and keep the larger of the two files.
Dir A
Dir B
file 1.txt
file 2.txt
file 3.txt
Dir C
file 4.txt
Dir D
file 5.txt
file 6.txt
Dir_Z
Dir Y
file 8.txt
Dir C
file 4.txt
Dir D
file 6.txt
I'm comfortable using a cmd file or powershell. Thank you in advance.
EDIT: Modify to include spaces in the director and file names and add what I have so far.
Here's what I've been able to do so far. It appears to work with the exception of when there are spaces in the directory or filenames.
#echo off
echo :: Combine two folders, Keep the larger of any given files, Delete the other
if "%1"=="" goto :NoParam
if "%2"=="" goto :NoParam
setlocal enabledelayedexpansion
set SOURCE_DIR=%1
set DEST_DIR=%2
for /R "%SOURCE_DIR%" %%S in (*) do (
echo Source: "%%S"
#echo off
for /f "delims=" %%R in ('call MakeRelative.cmd "%%S" "%SOURCE_DIR%"') do (
set FILE_REL="%%R"
set filename=%DEST_DIR%\%%R
For %%D in ("!filename!") do (
Set Name=%%~nxD
if exist %DEST_DIR%\%%R (
echo File Exists
if %%~zD lss %%~zS (
echo Destination %%~zD is smaller than Source %%~zS
call robocopy %%~dpS %%~dpD "!Name!" /MOV
) else del %%S && Echo File is not larger, Deleting %%S
) else call robocopy %%~dpS %%~dpD "!Name!" /MOV
)
)
)
Pause
goto :eof
:NoParam
echo.
echo Syntax: %0 [Source_DIR] [Dest_DIR]
goto :eof
I make use of the MakeRelative.cmd as outlined here: http://www.dostips.com/DtCodeCmdLib.php#MakeRelative
I apologize that this is not elegant.
I'm not much of a PowerShell developer, but this should do the work and is hopefully somewhat more obvious regarding what it's doing than the batch file.
if($args.length -ne 3) {
"usage: PowerShell ThisScriptName.ps1 -command ""SourceDirectory"" ""DestinationDirectory""";
Exit
}
function CopyFileIfSourceIsLarger($SourceFile, $DestinationFile) {
$S = New-Object IO.FileInfo($SourceFile.ToString());
$D = New-Object IO.FileInfo($DestinationFile.ToString());
if ($S.Length -gt $D.Length ) {
"Overwriting smaller file " + $D.FullName;
[IO.File]::Copy($S.FullName, $D.FullName, $true);
}
}
$SourceDir = New-Object IO.DirectoryInfo($args[1]);
$DestinationDir = New-Object IO.DirectoryInfo($args[2]);
$SourceDirectoryPath = $SourceDir.FullName;
$DestinationDirectoryPath = $DestinationDir.FullName;
"Source Path: " + $SourceDirectoryPath;
"Destination Path: " + $DestinationDirectoryPath;
$SourceItems = Get-ChildItem -recurse $SourceDir;
"Synchronizing Directory Structure..."
foreach ($dir in $SourceItems | ? { $_ -is [IO.DirectoryInfo] }) {
$sourceRelative = $dir.fullname.Substring($SourceDirectoryPath.length + 1);
if ([IO.Directory]::Exists([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) -eq $false) {
"Creating folder " + [IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative);
[IO.Directory]::CreateDirectory([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) | out-null;
}
}
"Synchronizing Files..."
foreach ($file in $SourceItems | ? { $_ -is [IO.FileInfo] }) {
$sourceRelative = $file.fullname.Substring($SourceDirectoryPath.length + 1);
if ([IO.File]::Exists([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative)) -eq $true) {
CopyFileIfSourceIsLarger ([IO.Path]::Combine($SourceDirectoryPath,$sourceRelative).ToString()) ([IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative).ToString());
} else {
"Creating file " + [IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative);
[IO.File]::Copy([IO.Path]::Combine($SourceDirectoryPath,$sourceRelative).ToString(),[IO.Path]::Combine($DestinationDirectoryPath,$sourceRelative).ToString()) | out-null;
}
}
"All done."
#Thanks to:
#"Identifying Object Types": http://powershell.com/cs/blogs/tobias/archive/2008/10/14/identifying-object-types.aspx
#"Exiting early from foreach": http://stackoverflow.com/questions/10277994/powershell-how-to-exit-from-foreach-object
#"Calling functions with multiple parameters" http://stackoverflow.com/questions/4988226/powershell-multiple-function-parameters
When the script runs, files 1,2,3,5,9, and 10 get copied, and file 6 gets overwritten because it is smaller in the destination.
Here is a batch file that sets up your test case (plus some extra folders to test spaces in the names). I have commented out the two lines that clear out directories A and Z for safety but you can remove the REM statements if you are unconcerned about deleting files in a folder A and Z in the current directory.
#ECHO OFF
REM Uncomment at own risk RD /S /Q A
REM Uncomment at own risk RD /S /Q Z
MD "A\B"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 1.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 2.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\B\file 3.txt"
MD "A\C"
echo aaaaaaaabbbbbbbbcccccccc>"A\C\file 4.txt"
MD "A\D"
echo aaaaaaaabbbbbbbbcccccccc>"A\D\file 5.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\D\file 6.txt"
MD "A\FOLDER E"
echo aaaaaaaabbbbbbbbcccccccc>"A\FOLDER E\file 9.txt"
echo aaaaaaaabbbbbbbbcccccccc>"A\FOLDER E\file 10.txt"
MD "Z\Y"
echo aaaaaaaabbbbbbbbcccccccc>"Z\Y\file 8.txt"
MD "Z\C"
echo aaaaaaaabbbbbbbbccccccccdddddddd>"Z\C\file 4.txt"
MD "Z\D"
echo aaaaaaaa>"Z\D\file 6.txt"

find through command prompt

How to find all the *.txt files in any directory(i.e. c:\,d:\ etc.) through command prompt?
c:
cd \
dir /s *.txt
d:
cd \
dir /s *.txt
Following will search from root directory and its all accessible sub folders regardless of the folder you currently in.
dir \*.txt /s
or
dir c:\*.txt /s
dir d:\*.txt /s
etc
Try using dir *.txt
setlocal ENABLEEXTENSIONS
FOR %%A IN (a b c d e f g h i j k l m n o p q r s t u v w x y z) DO #call :dumpdrive %%A
echo Done...
goto :EOF
:dumpdrive
FOR /R "%1:\" %%B IN (*.txt) DO #echo.%%~fB
goto :EOF

Resources