2

I have created a batch file that is copying filese and sub folders from source to destination. I have suppressed all sorts of errors. Following is the code which I am using currently:

Echo ****Copying...****
XCOPY "C:\Users\xxxxxx\Desktop\Source" "D:\xxxxx\Destination" /S /E /V /Y /R /C
Echo ****Files Copied Successfully...****
PAUSE

Now what I want to do is that once the file copying is complete it should display the list of all those files that were not copied to the destination due to some error.

Thanks in advance...

Loïc MICHEL
  • 22,418
  • 8
  • 68
  • 94

2 Answers2

1

Redirect output and errors to one file with >logfile.txt 2>&1 and then postprocess the logfile.txt using for /F command:

@ECHO OFF >NUL
@SETLOCAL enableextensions disabledelayedexpansion
set "source=d:\bat\*.txt"
set "target=d:\xxx\ddd"
xcopy "%source%" "%target%" /i /s /e /y /c>%temp%\xcopyall.txt 2>&1

set /A "goodfiles=-1"
set /A "bad_files=0"

set "filename=%source:~0,2%"
set "errdescr=%filename%"
for /f "tokens=* delims=)(" %%G in ('type "%temp%\xcopyall.txt"') do (
  set "errdescr=%%G"
  call :posterr 
)
echo %bad_files% File[s] not copied
:::: %goodfiles% File[s] copied
@ENDLOCAL
goto :eof

:posterr
set "errdescr=%errdescr:(=[%"  
set "errdescr=%errdescr:)=]%"
rem previous line == a filename?  
if /i "%filename:~0,2%"=="%source:~0,2%" (
  rem current line == a filename?
  if /i "%errdescr:~0,2%"=="%source:~0,2%" (
      set /A "goodfiles+=1"
  ) else (
      rem rem current line == 'nnn File[s] copied'?
      if /i "%errdescr:File[s] copied=%"=="%errdescr%" (
        set /A "bad_files+=1"
        echo %filename:&=^&% %errdescr:&=^&%
    ) else ( 
        set /A "goodfiles+=1"
    )
  )
)
set "filename=%errdescr%"
goto :eof

Fixed:

  • attended end of logfile, i.e. nnn File(s) copied text
  • literal value "d:" replaced by dynamic "%source:~0,2%"
  • auxiliary counters for both (not)copied items with improved test logic (cf. comments in code)
  • tested for file names containing !, %, & (see output), by virtue of dbenham, thanks...

The script still might appear unsufficient, one could refine it for possible more complicated scenarios (tested for Access denied error at target side only).

Output:

d:\bat>vbserr2
D:\bat\CPs2\unicMacCE.txt Access denied
D:\bat\files\11per%cent.txt Access denied
D:\bat\files\12per%cent%.txt Access denied
D:\bat\files\13per%OS%cent.txt Access denied
D:\bat\files\14per%%OS%%cent.txt Access denied
D:\bat\files\15per%3cent.txt Access denied
D:\bat\files\16per%%3cent.txt Access denied
D:\bat\files\17per%Gcent.txt Access denied
D:\bat\files\18per%%Gcent.txt Access denied
D:\bat\files\1exclam!ation.txt Access denied
D:\bat\files\21ampers&nd.txt Access denied
D:\bat\files\22ampers&&nd.txt Access denied
D:\bat\files\2exc!lam!ation.txt Access denied
D:\bat\files\rand.txt Access denied
14 File[s] not copied

However, Xcopy has been deprecated already (although still available now). Use other tools (such as Robocopy) instead.

Community
  • 1
  • 1
JosefZ
  • 22,747
  • 4
  • 38
  • 62
  • 1
    Well done, except this will fail if any file name contains `!` because expansion of FOR variables will corrupt values containing `!` if delayed expansion is enabled. Can be solved by disabling at top, and within loop, enable before IF and ENDLOCAL after IF. Also, the user no longer gets a log of successful files - easily fixed. – dbenham Jan 12 '15 at 14:43
  • Thanks JosefZ. This is what I was looking for. – user3368855 Jan 13 '15 at 06:14
0

You can detect if a file is an error by looking for a line that is followed by another line that does not begin with a digit, and whose 2nd character is not a colon.

In order to do this programmatically, you must redirect stderr to stdout using 2>&1 to get all data in one stream.

JosefZ redirected everything to a file, and post-processed with a FOR /F loop to extract the error messages.

Below is a more efficient solution that pipes the output to JREPL.BAT - a hybrid JScript/batch utility that performs a regular expression search/replace on text. JREPL.BAT is pure script that runs natively on any Windows machine from XP onward.

xcopy "C:\Users\xxxxxx\Desktop\Source" "D:\xxxxx\Destination" /s /e /v /y /r /c /I 2>&1 | ^
jrepl "^(.+\n)(\D[^:][^\r\n]*)\r\n/^.*\n" "'***'+$3+': '+$2/stderr.Write($0);''" /m /j /t "/"

I used line continuation just to make the code a bit easier to read.

  • The /M option allows searches to span multiple lines.
  • The /J option treats the replacement value as JScript code
  • The /T option is similar to the unix tr command. Both search and replace arguments contain a like number of values, delimited by /. The first replace value is used if the first search term matches, else the 2nd replace value is used for the 2nd search term.

The first search term looks for a file name, followed by an error message. If found, then the two lines are replaced by a single line using the format ***eror message: filePath. Since this is a multi-line search, the result is not displayed until the entire search/replace is done.

The second search term (only used if first failed to match) simply matches the entire line. This line is immediately output to stderr before replacing the value with an empty string.

The end result is that successful files and the summary are first output to the console using stderr, followed by the error files with error message on stdout.

Below is a test sample output:

D:test.bat
D:test.js
D:test.log
D:test.txt
D:test.txt.bad
D:test.vbs
D:test2.bat
D:test3.bat
D:test\test(1).txt
D:test\test(10).txt
D:test\test(11).txt
D:test\test(2).txt
D:test\test(3).txt
D:test\test(5).txt
D:test\test(6).txt
D:test\test(7).txt
D:test\test(8).txt
D:test\test(9).txt
D:test\test.txt
19 File(s) copied
***Access denied: D:test.csv
***Access denied: D:test\test(4).txt

The output is bass-ackwards in that error messages are on stdout, and success messages are on stderr.

If desired, you can capture the entire result in log file by appending >xcopy.log 2>&1

Or you could separate the log into good and bad using >xcopy.err 2>xcopy.log.

Community
  • 1
  • 1
dbenham
  • 119,153
  • 25
  • 226
  • 353