"rd" exits with errorlevel set to 0 on error when deletion fails, etc

11,886

Solution 1

Wow, this is the 2nd case I've seen where ERRORLEVEL is not set properly! See File redirection in Windows and %errorlevel%.

The solution is the same as for detecting redirection failure. Use the || operator to take action upon failure.

rd testdir || echo The command failed!

The bizarre thing is, when you use the || operator, the ERRORLEVEL is then set properly to 145 if the folder was not empty, or 2 if the folder did not exist. So you don't even need to do anything. You could conditionally "execute" a remark, and the errorlevel will then be set properly.

rd testdir || rem
echo %errorlevel%

I thought the above gave a complete picture. But then a series of comments below demonstrated there are still potential problems when /RD /S is used. If a file or subfolder under the parent folder is locked (at any level under parent) then RD /S /Q PARENT && echo removed || echo failed will print out an error message, but the && branch fires instead of the || branch. Very unfortunate. If the command fails because the parent folder itself is locked, then || will properly fire and set the ERRORLEVEL.

It is possible to detect failure in all cases by swapping stderr with stdout and piping the result to FINDSTR "^". If a match is found, then there must have been an error.

3>&2 2>&1 1>&3 rd /s test | findstr "^" && echo FAILED

The swap of stderr and stdout is important when /q is missing because it allows the "Are you sure (Y/N)?" prompt to be visible on stderr, separate from the error message on stdout.

Solution 2

rd does not set errorlevel to zero - it leaves errorlevel intact: f.e. if previous operation ends in positive errorlevel and rd finishes successfully it leaves errorlevel unchanged. Example: error levels of robocopy below 4 are warnings and not errors and can be ignored so the following code may end with error even when the directory was deleted successfully:

robocopy ...
if errorlevel 4 goto :error
rd somedir
if errorlevel 1 goto :error

Solution: ignore the error and check if the directory still exists after rd:

rd somedir
if exist somedir goto :error
Share:
11,886
etuardu
Author by

etuardu

Now, alduce me to introlow myself. I'm sorry. Alself me to myduce introlow. Introme tolose mylow alself. Alme to you introself mylowduce. Excuse me a moment. #SOreadytohelp

Updated on June 13, 2022

Comments

  • etuardu
    etuardu almost 2 years

    I'm writing a batch (.bat) script and I need to handle the case in which the deletion of a folder fails. I'm using %errorlevel% to catch the exit code, but in the case of the rd command it seems not to work:

    C:\Users\edo\Desktop>rd testdir
    Directory is not empty
    
    C:\Users\edo\Desktop>echo %errorlevel%
    0
    

    Why? What do you suggest?

  • etuardu
    etuardu almost 12 years
    Well, that just worked. I suppose the problem is related to %errorlevel% and has nothing to do with rd. I think I should rewrite my error handlings using this structure for a more deterministic behaviour. Thanks!
  • Andreas Vergison
    Andreas Vergison about 9 years
    This works fine for codes 2 and 145, but in case of "Access denied" or "The process cannot access the file because it is being used by another process" then it just leaves ERRORLEVEL unchanged. :(
  • dbenham
    dbenham about 9 years
    @AndreasVergison - Thanks! I updated my answer with your info.
  • rychu
    rychu about 9 years
    Also about robocopy: be sure not to reset the errorlevel with set errorlevel=0 after you check it's not >= 4 because this command creates an environment variable that permanently overwrites the internal errorlevel. Read here
  • Daniel Kienböck
    Daniel Kienböck about 9 years
  • Mark
    Mark about 7 years
    I just tried it on windows 10. I have the same problem @AndreasVergison has. I don't get an errorlevel (or more accurately, I get "OK") even though it fails to delete because "the file is being used by another process". I get the error message in the console but it doesn't have an ErrorLevel
  • dbenham
    dbenham about 7 years
    @Mark - Hmmm, I just double checked on my Win 10 machine and the || solution works fine for me, with the expected ERRORLEVEL of 32. The simplest syntax to get the correct result is rd "yourFolder" || rem, and then on the next line echo %errorlevel%. But if that code is within a parenthesized block, then you must enable delayed expansion and use echo !errorlevel! instead.
  • Stefan
    Stefan almost 7 years
    This does not work with parameter /s for me. Can someone confirm this?
  • Barniferous
    Barniferous almost 7 years
    @Stefan I just tried on Windows 7 and this worked for me: (call ) & rd /s aaaa && echo. || rem I got both the In Use message as well as errorlevel being properly set. Could you be more specific about exactly what syntax didn't work, and what OS you're using?
  • Aisah Hamzah
    Aisah Hamzah over 6 years
    @Barniferous, it depends on the error. I can confirm @Stefan's findings. I.e., rd /q /s tmp && echo OK || echo ERROR !errorlevel! will print "The process cannot access the file because it is being used by another process." and then "OK" (no errorlevel). Other errors, like the ERROR 2 from dbenham's post are reported correctly, though. Repro: open command prompt and cd to a dir. In another command prompt, try to delete that dir with dbenham's solution. No errorlevel, but error is printed... :S. Tested on Windows 7.
  • it3xl
    it3xl over 5 years
    Conditional Execution || doesn't work, at all, if an internal folder is blocked for an access Access is denied. The RD just have 0 exit code.
  • Alexander Samoylov
    Alexander Samoylov over 5 years
    I think the above mentioned example from @dbenham worked for "Access Denied" case only because it was a non-recursive removal. It was an "Access denied" about "junk" directory itself and not about its internals. If the "Access denied" is caused by some file inside the directory then ERRORLEVEL is 0. I think this is may be because of the buggy function from "del" (stackoverflow.com/questions/22953027/…). Maybe during the recursive removal Windows tries to use "del" for each file inside the directory and it always returns 0 unfortunately.
  • dbenham
    dbenham over 5 years
    @AlexanderSamoylov - Great analysis. I did some more tests, and you are absolutely correct. I'll update my answer when I get a chance.
  • Doktor J
    Doktor J almost 5 years
    This is the best solution IMO. Instead of trying to do convoluted workarounds, swapping outputs, and other crap trying to detect all the odd cases... just see whether the directory is still there; if it is, you had a problem.
  • user1254127
    user1254127 over 2 years
    I am reminded of the behavior of POSIX rm -f (forcibly remove), which not only disregards permissions to the extent possible, but also suppresses error codes, always exiting 0. I think this is to make clean up actions as robust as possible, especially when running with set -e, as in a Makefile recipe. (Obviously, in a recipe, a minus prefixed to a command line achieves the same result). Perhaps cmd's treatment of rd and del is motivated similarly.