0

I've been investigating this a lot with threads on StackOverflow and the like, but although I feel I'm close to the solution, this problem is giving me headaches.

What I'm trying to do: When a specific external hard drive is connected (distinguished via VolumeSerialNumber over WMIC), the drive letter is found out, and mirroring is done via robocopy. The script is executed via double-click. This is what I have so far:

FOR /F "skip=1" %%i in ('wmic logicaldisk where VolumeSerialNumber^="XXXXXXXX" get deviceid 2^>nul') DO (
    SET y=%%i
    IF [%y%]==[] GOTO hdmissing
    SET "backuphd=%%i"
    GOTO endfor
)
:endfor

robocopy "C:\Users\Herbert\Documents" "%backuphd%\Backup\Documents" /MIR

ECHO Backup done
ECHO end

:hdmissing
ECHO Couldn't find external drive

:end
PAUSE

This way, the external HD is never detected (%y% is always an empty string). However, if I execute the script twice in the same console session, everything works as expected. But I want it to work at the first execution.

This is what I've tried so far:

  • Put SET y=dummy at the beginning of the script. The HD is always found, triggering a backup to C: if the HD is not actually connected (apparently SET y=%%i doesn't alter y?)
  • Change %y% to !y! - The HD is always found, again

2 Answers 2

2

Generation 3,576 of the delayed expansion problem, compounded by a contaminated environment.

There's no setlocal apparent, so y remains set in the environment after the first run - hence the 'later run characteristics different from first run' phenomenon.

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 - the same thing applies to a FOR ... DO (block).

Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.

Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.

The key in your case appears to be no setlocal enabledelayeexpansion and !y! - because !y! is just that - a literal string !y! unless delayedexpansion is invoked by the setlocal command.

(having said that,

IF [%%i]==[] GOTO hdmissing

would work just as well, as would

SET "y=%%i"
IF not defined y GOTO hdmissing

because if [not] defined var operates on the run-time value of var. "quoting the set arguments" ensures that any stray trailing spaces on the line are not included in the value assigned )

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I wasn't fully aware about how the delayed expansion thing worked. If I had enough reputation, I would upvote your answer, but I solved it differently (see my detailed answer). Escaping under Delayed Expasion is a nightmare. IF [%%i]==[] GOTO hdmissing doesn't work as well, because WMIC returns a <CR> at the end, and this seems to be not interpreted as line break, but as own character. I figured SET y=%%i gets rid of <CR>. IF not defined y GOTO hdmissing didn't work either on my machine.
0

As Magoo already pointed out, !y! doesn't work, since I forgot to enable Delayed Expansion. However, enabling it requires you to escape certain characters, which seemed quite irritating and tedious to me. It could be possible to just enclose the command in the FOR-loop with double quotes, as done in the two edits of this question, but I found out after solving it differently.

What I did was moving the block

IF [%y%]==[] GOTO hdmissing
SET "backuphd=%%i"

after :endfor. This way, it's outside of the for loop and %y% gets expanded accordingly.

Mind that this solution works only because I need just one item, and because of the programming flow.

Other useful approaches might be found in this question.

However, if you want to do the same thing: easier (though not as robust) solutions without WMIC might include

  • Putting a file with a specific name on the hard drive and checking for it with IF EXIST before backing up, hoping the user will not delete it, and the HD is connected to the same drive letter.
  • Putting the batch script on the drive itself and work with relative path names (optional: put a shortcut on the Desktop and hope again the drive is always under the same letter)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.