Detecting Removable drive letter in CMD - windows

I'm trying to write a script, which will detect the letter of my USB Removable Drive called "UUI" and then create folder on it. I've written few commands for CMD which, when run separately, work. However when I put them into a bat file, I always get some errors. Here are the commands in a bat file:
for /F "tokens=1 delims= " %i in ('WMIC logicaldisk where "DriveType=2" list brief ^| c:\windows\system32\find.exe "UUI"') do (echo %i > drive.txt)
set /p RemovableDriveLetter2= < drive.txt
del /F /Q drive.txt
set RemovableDriveLetter=%RemovableDriveLetter2:~0,1%
%RemovableDriveLetter%:
md MyNewFolder
cd MyNewFolder
When I go to cmd.exe and run the file by calling "myScript.bat" or "call myScript.bat", I get an error:
C:\Users\UUI\Desktop>myScript.bat
\windows\system32\find.exe was unexpected at this time.
C:\Users\UUI\Desktop>for /F "tokens=1 delims= " \windows\system32\find.exe "UUI"') do (echo i > drive.txt)
C:\Users\UUI\Desktop>
I can see that MyNewFolder was not created. However, when I copy all lines and run them in CMD as such (e.g. not in the .bat file) and run them one by one, it is fully functional within the cmd.exe instance.
How can I create bat a file, which will successfully run and detects the drive letter of my removable drive without issues? Or how can I solve the error "\windows\system32\find.exe was unexpected at this time."?

You need to double the % sign used to mark a FOR loop control variable in a batch script (.bat or .cmd), i.e. use %%i instead of %i used in pure CLI.
However, there is another possible approach how-to parse wmic output.
See also Dave Benham's WMIC and FOR /F: A fix for the trailing <CR> problem
#echo OFF
SETLOCAL enableextensions
set "USBCounter=0"
for /F "tokens=2 delims==" %%G in ('
WMIC logicaldisk where "DriveType=2" get DeviceID /value 2^>NUL ^| find "="
') do for /F "tokens=*" %%i in ("%%G") do (
set /A "USBCounter+=1"
echo %%i
rem your stuff here
)
echo USBCounter=%USBCounter%
rem more your stuff here
ENDLOCAL
goto :eof
Here the for loops are
%%G to retrieve the DeviceID value;
%%i to remove the ending carriage return in the value returned: wmic behaviour: each output line ends with 0x0D0D0A (CR+CR+LF) instead of common 0x0D0A (CR+LF).
One could use Caption or Name instead of DeviceID:
==>WMIC logicaldisk where "DriveType=2" get /value | find ":"
Caption=F:
DeviceID=F:
Name=F:
Note there could be no or more disks present having DriveType=2:
==>WMIC logicaldisk where "DriveType=2" get /value | find ":"
No Instance(s) Available.
==>WMIC logicaldisk where "DriveType=2" list brief
DeviceID DriveType FreeSpace ProviderName Size VolumeName
F: 2 2625454080 3918512128 HOMER
G: 2 999600128 1029734400 LOEWE
Script output for no, then one and then two USB drive(s), respectively:
==>D:\bat\SO\31356732.bat
USBCounter=0
==>D:\bat\SO\31356732.bat
F:
USBCounter=1
==>D:\bat\SO\31356732.bat
F:
G:
USBCounter=2
==>

Related

windows cmd get USB drive letter automatically without batch file

I have a PC in which I insert a USB Drive and execute a batch script, but the USB Drive interacts with the computer, so I need to know in which Drive Letter it is mounted.
I tried this
for /f "tokens=2,3 delims= " %%A in ('WMIC logicaldisk where "DriveType=2" get /value | find "Caption="') do (
set drive=%%A
)
But I get an error stating Did not expect %%A at this moment.
I know this works
#echo off
for /F "usebackq tokens=1,2,3,4 " %%i in (`wmic logicaldisk get caption^,description^,drivetype
2^>NUL`) do (
if %%l equ 2 (
echo %%i is a USB drive.
)
)
But it implies having to save it in a .bat file, and I don't want to do that.
Ideally, what I want to do is to be able to switch to the USB Drive without needing to launch it from a .bat file, so this command should allow me to get the Drive Letter. Afterwards, the script continues.
Thanks to the #aschipfl I managed to find out the right command:
for /F "usebackq tokens=1,2,3,4 " %i in (`WMIC logicaldisk where "DriveType=2" get /value ^| find "Caption="`) do (set drive=%i)
set drive=%drive:~8,9%
And now %drive% is the letter in which the drive is mounted. If there are more than one mounted letters, it will be the last one.

WMIC: how to use "process call create" with the current working directory

I want to use wmic Process call create to open a CMD at current working directory. But when I add "%~dp0" to specify directory the following command output nothing.
for /F "tokens=2 delims==; " %%I in ('wmic Process call create "cmd.exe"^,"%~dp0" ^| find "ProcessId"') do echo PID = %%I
As I know %~dp0 will end with \, so %~dp0 will be like C:\Users\.
If I remove \ It will works. E.g: The following command will work.
for /F "tokens=2 delims==; " %%I in ('wmic Process call create "cmd.exe"^,"C:\Users" ^| find "ProcessId"') do echo PID = %%I
How can I use wmic Process call create with dynamic working directory?
I found the solution. I have to add a trailing dot (.).
%~dp0 to %~dp0.
for /F "tokens=2 delims==; " %%I in ('wmic Process call create "cmd.exe"^,"%~dp0." ^| find "ProcessId"') do echo PID = %%I
Read more: How to get the path of a batch script without the trailing backslash in a single command?

how can I program the volume serial number into a variable for a batch file?

This Win7 batch file code retrieves the Serial Numbers for all drives on the computer and displays them in a command window, however
I am unable to isolate just the drive %~d0 from which the code is running and capture the volumeserialnumber into a %variable_%.
rem -----------Test volumeSerialNumber.BAT------------------
#echo off
wmic logicaldisk where drivetype=3 get volumeserialnumber
pause
for /F "skip=1 delims=" %%j in ("wmic logicaldisk where deviceid = '%~d0' get volumeserialnumber") do (
set SERIAL=%%j
goto :DONE
)
:DONE
echo SERIAL=%SERIAL%
echo %SERIAL%
pause
if "%SERIAL%"=="The Serial Number of Drive C" (
echo "Success!"
)
It looks like you just needed to change some single and double quotes.
Try:
For /F "Skip=1 Delims=" %%j In ('WMIC LogicalDisk Where "DeviceID='%~d0'" Get VolumeSerialNumber') Do ...

WMIC Command Not Working in For Loop of Batch File

I have a batch file that I am trying to run, but I keep getting an error. I think that this question is similar to I can't get the right syntax to use WMIC in batch file, but dbenham's answer doesn't completely work in my case because I am piping to findstr. Here is a part of the batch file (the part it is hanging on):
for /F %%I in ('wmic nic where 'Manufacturer!="Microsoft" and Macaddress IS NOT NULL' get index ^| findstr /r [0-9]') do (
echo %%I
)
The wmic command works just fine if you run it from cmd or it's own line of a batch file, but I cannot get it to run in the for loop. Can anyone help me out here?
Thanks,
John
Try it this way:
for /f "tokens=2 delims==" %%I in (
'wmic nic where "manufacturer!=\"Microsoft\" and macaddress is not null" get macaddress /format:list 2^>NUL'
) do echo %%I
You have to backslash-escape your quoted stuff where your quotes are nested.
There is a feature in WMIC that it sometimes waits for user input.
If you replace
wmic
in your example, with
echo. ^| wmic
it will allow the command to complete

Why is the FOR /f loop in this batch script evaluating a blank line?

I'm trying to write a batch script that obtains (among other things) a list of all of the disk drives the computer has. The basic code looks something like this:
REM Build the list of disk drives to monitor
SETLOCAL enabledelayedexpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\\|"
)
I pretty obviously build two lists with slightly different formats for use later. When I run this, however, the output I get looks something like this:
C|D|E||
C:\\|D:\\|E:\\|:\\|
Now, I expect the trailing pipe in both cases and I can manage that, but I'm really confused why there is an extra blank entry in there. If I run the wmic command manually, I can see that there is indeed a blank line at the end of the output, but my understanding is that /f was specifically supposed to ignore blank lines.
If I turn ECHO on, it looks like that last line is just coming in as a carriage return/newline or similar. Is there a way to do what I'm expecting? Am I missing something? I tried to write an if condition in the loop to exclude this last line, but it was... funky and never worked. I appreciate any/all help.
I just came over this topic. I've been using findstr /v to exclude empty lines:
FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
In this case the last iteration produces not an empty item, and you get your output of C|D|E|| only with echo %DISK_DATABASES%,
but echo !DISK_DATABASES! will output ||D|E|??
That's because the last element is a single <CR> character.
And <CR> characters are directly removed after the percent expansion, but not with delayed expansion.
You could avoid this, using the percent expansion to remove them
setlocal EnableDelayedExpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
set "item=%%a"
call :removeCR
if not "!item!"=="" (
SET "DISK_DATABASES=!DISK_DATABASES!!item!|"
SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!!item!:\\|"
)
)
goto :eof
:removeCR
:removeCR
set "Item=%Item%"
exit /b
According to http://ss64.com/nt/for_f.html
Many of the newer commands and utilities (e.g. WMIC) output text files in unicode format, these cannot be read by the FOR command which expects ASCII.
To convert the file format use the TYPE command.
So it appears that WMIC and FOR don't play nice together.
I discovered a more efficient and more reliable method to strip the unwanted <CR> from the end of each line. No temp file, and no CALL needed.
I don't understand the mechanism of how FOR /F converts the WMIC unicode output into ASCII. Normally FOR /F cannot read unicode. But however it works, each converted line ends with <CR><CR><LF>. FOR /F breaks lines at each <LF>, and then if the last character in the line is <CR> it strips that last <CR>, in this case leaving behind the unwanted <CR>.
The solution is to simply pass each line through one more FOR /F :-)
#echo off
setlocal enableDelayedExpansion
for /f "skip=1 delims=" %%A in (
'wmic logicaldisk where "drivetype=3" get deviceid'
) do for /f "tokens=1 delims=:" %%B in ("%%A") do (
set "disk_databases=!disk_databases!%%B|"
set "drives_to_monitor=!drives_to_monitor!%%B:\\|"
)
This method is more reliable then using normal expansion because you don't have to worry about quoting or escaping special characters. For example, The CALL method that uses normal expansion cannot handle a string like "this & that" & the other. But this method has no problem with such a string.
Add ^| findstr . and you will get only not blank lines
REM Build the list of disk drives to monitor
SETLOCAL enabledelayedexpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in (
'"WMIC logicaldisk WHERE drivetype=3 GET deviceid" ^| findstr .') do (
SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\|"
)
My standard idiom for dealing with this is to write the output from WMIC to a temp file, then use TYPE (which reduces UTF16 to ASCII) to feed that into FOR, like this:
:: Standard environment setup
setlocal enabledelayedexpansion
:: Every variable whose name starts with "tf" will identify a temporary
:: file - remove any such variables inherited from the parent environment
for /f %%V in ('set tf') do set %%V=
:: Create some temporary filenames. Prefix all of them with this script's
:: own name to avoid clashes with those owned by other scripts.
for /l %%I in (1,1,4) set tf%%I="%temp%\%~n0-temp%%I.txt"
:: Use temp file to work around coding mismatch between WMIC out and FOR in
wmic product where "name like 'Microsoft Office %% 2010'" get packagecache >!tf1!
for /f "skip=1" %%P in ('type !tf1!') do if exist "%%~P" msiexec /x "%%~P" /passive /norestart
:: Before quitting script, clean up temporary files
for /f %%V in ('set tf') do if exist "%%~V" del /f /q "%%~V"
endlocal
Run the following command:
wmic blah /value | find "=" >> wherever
Output will be:
field=value
Note that there will be no extra lines.

Resources