0

I am using three functions. One is getting the list of drives:

REM list the drives
:list_disk
ECHO List of drives

set n=0
for /f "skip=1 delims=" %%a in ('wmic logicaldisk get DeviceID^') do (
    set nom[!n!]=%%a
    set /A n+=1
)
set /A "N=%n%/2"
FOR /L %%i IN (0,1,%N%) DO (
    echo !nom[%%i]!
    echo/
)
EXIT /B 0

The second is proposing to the user to choose which drives should be processed in the next function:

REM choice of drives
:choix nom
echo Choice
FOR /L %%i IN (0,1,%N%) DO (
    echo Do you want to raise the drive !nom[%%i]!
    echo (y/n^) ?

    SET c=n
    SET /P c=
    if !c!==y call :HASH i
)

EXIT /B 0

And the last one uses the index of the array to process the chosen drive:

REM fingerprinting
:HASH
ECHO Fingerprinting in progress ...
echo !nom[%%i]!

ECHO progam integration fingerprint :

EXIT /B 0

The problem is the last echo !nom[%%i]! which doesn't work as expected.

Why does it not print which drive is selected?

akmot
  • 63
  • 8
  • Are you sure it's not just a case of a linefeed from WMIC's output being defined to the last index? - that's the entire reason the macro I posted in answer to your last question contains a secondary for loop during capture of command output - to handle this edge case – T3RR0R Jul 26 '22 at 08:28

1 Answers1

0

The main problem is caused by passing the string i instead of the value of loop variable i to the subroutine HASH by using if !c!==y call :HASH i instead of if !c!==y call :HASH %%i and referencing the value of the loop loop variable with echo !nom[%%i]! not existing at all in the context of the subroutine execution instead of the value passed to the subroutine with echo !nom[%1]!.

But there are several other problems with this code like ignoring the last drive because of indexing the drives wrong or usage of set /p although using choice would be definitely better here as more fail-safe and secure.

It is also advisable to reference executables with well-known path in batch files with their fully qualified file names making the execution of the batch file faster as cmd.exe does not need to search for the executables using the environment variables PATH and PATHEXT and so avoiding lots of file system accesses during execution of the batch file.

A better code is:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem list the drives
:list_disk
echo List of drives
echo(
set n=-1
for /F "tokens=2 delims==:" %%i in ('%SystemRoot%\System32\wbem\wmic.exe LOGICALDISK GET DeviceID /VALUE') do (
    set /A n+=1
    set nom[!n!]=%%i
)
for /L %%i IN (0,1,%n%) do echo !nom[%%i]!:

rem choice of drives
:choix nom
echo(
echo Choice
echo(
for /L %%i in (0,1,%n%) do (
    %SystemRoot%\System32\choice.exe /C NY /N /M "Do you want to raise the drive !nom[%%i]!: (Y/N)?"
    if errorlevel 2 call :HASH %%i
)
exit /B 0

rem fingerprinting
:HASH
echo(
echo Fingerprinting in progress for drive !nom[%1]!: ...
rem Call of the command to generate the fingerprint for drive !nom[%1]!
echo program integration fingerprint:
echo(
exit /B 0

The first FOR loop assigns now only the drive letter without colon to the environment variable with name[x] whereby x is in range 0 to n, for example 0 to 3 on four drives output by wmic.

The usage of wmic option /VALUE and the for /F options tokens=2 and delims==: avoids the problem with wrong parsed UTF-16 Little Endian output of wmic by for. The command for interprets 0D 00 0A 00 (UTF-16 LE encoded carriage return + line-feed) wrong as 0D 0D 0A (carriage return + carriage return + line-feed). This wrong Unicode text parsing does not matter because of assigning to the loop variable i only the drive letter between DeviceID= and : from the output of wmic.

Therefore the number of processed lines and created environment variables assigned to n is correct and no division by two is necessary anymore. n is now the last index number used on definition of an environment variable with a drive letter.

The FOR loop for the choice uses now the command CHOICE to prompt the user for the Yes/No choice. The letters Y in the command line can be replaced by O for the French version, i.e. using /C NO and (O/N). For more details see my answer on How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?

Another solution using mountvol.exe instead of wmic.exe which is not available anymore in Windows 11 starting with build 22572:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
rem list the drives
:list_disk
echo List of drives
echo(
set n=-1
for /F "delims=: " %%i in ('%SystemRoot%\System32\mountvol.exe ^| %SystemRoot%\System32\find.exe ":\" ^| %SystemRoot%\System32\sort.exe') do (
    set /A n+=1
    set nom[!n!]=%%i:
)
for /L %%i IN (0,1,%n%) do echo !nom[%%i]!

rem choice of drives
:choix nom
echo(
echo Choice
echo(
for /L %%i in (0,1,%n%) do (
    %SystemRoot%\System32\choice.exe /C NY /N /M "Do you want to raise the drive !nom[%%i]! (Y/N)?"
    if errorlevel 2 call :HASH %%i
)
exit /B 0

rem fingerprinting
:HASH
echo(
echo Fingerprinting in progress for drive !nom[%1]! ...
rem Call of the command to generate the fingerprint for drive !nom[%1]!
echo program integration fingerprint:
echo(
exit /B 0

In this case the environment variables are defined with drive letter and colon as the colon is already added on line set nom[!n!]=%%i:. It would be also possible to use delims=\ instead of delims=: and set nom[!n!]=%%i to get the environment variables defined with drive letter and colon.

Another solution using fsutil.exe to get the drives with colon for assigning them to the environment variables is:

for /F "tokens=1*" %%i in ('%SystemRoot%\System32\fsutil.exe fsinfo drives') do for %%k in (%%j) do for /F "delims=\" %%l in ("%%k") do (
    set /A n+=1
    set nom[!n!]=%%l
)

for /F "delims=\" %%l in ("%%k") do could be omitted and set nom[!n!]=%%l changed to set nom[!n!]=%%k to get the drives with colon and with the backslash assigned to the environment variables, i.e. C:\, D:\, ... instead of C:, D:, ...

The other lines of this third solution getting the drives are the same as in second solution with mountvol.exe.

Note: The solution using fsutil.exe expects an output like:

Drives: C:\ D:\ R:\

That is the output on an English Windows. If there are multiple space separated words left to first output drive ending finally with : instead of Drives: it is better to use "tokens=1* delims=:" instead of just "tokens=1*" in first for /F command to ignore the string left to first colon in output of fsutil.exe instead of left to first space and process the remaining part of the line with the space separated drives with the second for command.

To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

  • call /?
  • choice /?
  • echo /?
  • endlocal /? ... not used explicitly, but run implicitly by cmd.exe
  • exit /?
  • find /?
  • for /?
  • fsutil /?
  • fsutil fsinfo /?
  • mountvol /?
  • rem /?
  • set /?
  • setlocal /?
  • sort /?
  • wmic /?
  • wmic logicaldisk /?
  • wmic logicaldisk get /?
Mofi
  • 46,139
  • 17
  • 80
  • 143