I'm trying to set up a batch file that will iterate through the local drives on a PC and get the Volume Name of each drive into an Environment Variable that I can then use for further processing.
Here's what I've got so far, but it doesn't return the correct value for the volume label into the VLABEL1 variable inside the For loop.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /f "tokens=2 delims==" %%D in ('wmic logicaldisk where "drivetype=3" get name /format:value') do (
echo Processing Drive %%D
call :GetLabel %%D VLABEL1
echo The Label of Volume %%D is !VLABEL1!
echo.
)
endlocal
goto :EOF
:GetLabel
setlocal
for /f "tokens=5*" %%A in ('vol "%~1"^|find "Volume in drive "') do (
set VOLLABEL=%%B
)
set %2=%VOLLABEL%
endlocal
goto :EOF
Needles to say I've tried various combinations & permutations, but without success, so I'd appreciate any suggestions.
Try to run this from command line and you will see the reason
for /f "tokens=2 delims==" %a in ('wmic logicaldisk where "drivetype=3" get name /format:value') do echo .[%a].
There is an aditional carriage return at the end of each of the lines.
So, it is necessary to eliminate it before calling your subroutine.
for /f "tokens=2 delims==" %%A in (
'wmic logicaldisk where "drivetype=3" get name /format:value'
) do for %%D in (%%A) do (
....
)
...
The aditional for loop removes the CR so now you have the correct value in the variable to call your subroutine.
Now, inside your subroutine,
set %2=%VOLLABEL%
endlocal
The endlocal cancels the assignment of the upper line. It is its mission, cancel changes made in the environment since the previous setlocal. So, it is necessary to both, change the variable and cancel the non required changes. To do so, the fact that the parser works on lines can be used. So the code should be written as
endlocal & set "%2=%VOLLABEL%"
The full line is readed, at parse time all the value reads are replaced with values and then the line is executed. So, when the second command in the line (the set) is executed, there is no access to the %VOLLABEL% variable. This read has been replaced with the value of the variable before the endlocal were executed.
Related
This question already has an answer here:
Variables are not behaving as expected
(1 answer)
Closed 2 years ago.
I'm trying to write a script but I'm really bad at writing the batch scripts. I'm trying to create a script that recognizes local hard drives and puts the letter of those hard drives in a variable, and after that I use that variable in another for loop to decrypt the hard drive if it's encrypted.
Example:
:check
for /f "tokens=2 delims==" %%d in ('wmic logicaldisk where "drivetype=3" get name /format:value') do (
set vvv=%%d
for /f "tokens=1,*" %%A in ('manage-bde -status %vvv% ^| findstr Conversion') do set var1=%%B
Rem Try to find if value is Encrypted or not
echo %vb1%|find "Encrypted" >nul
if errorlevel 1 ( goto :check) else ( goto :decrypt_c))
#Echo Off
:check
for /f "tokens=2 delims==" %%d in ('wmic logicaldisk where "drivetype=3" get name /format:value') do (
echo %%d
manage-bde -off %%d )
pause
That code works fine until echo %%d, when I try to put it into manage-bde command does not work...When I have for example 3 hard drives, I will do loop and turn off BitLocker for all partitions. Thanks!
Perhaps you'd be better off using the Win32_EncryptableVolume Class.
This example will create variables, e.g. %EncryptedDriveLetter[1]%, %EncryptedDriveLetter[2]%, %EncryptedDriveLetter[3]% etc., with the respective content of e.g. C:, E:, F:
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
For /F Delims^== %%G In ('2^> NUL Set EncryptedDriveLetter[')Do Set "%%G="
For /F Delims^= %%G In ('^""%SystemRoot%\System32\wbem\WMIC.exe" ^
/NameSpace:\\root\CIMv2\Security\MicrosoftVolumeEncryption Path ^
Win32_EncryptableVolume Where ^
"ConversionStatus!='0' And EncryptionMethod!='0' And VolumeType<'2'" ^
Get DriveLetter 2^> NUL ^| "%SystemRoot%\System32\find.exe" ":"^"'
)Do (Set /A i+=1 & SetLocal EnableDelayedExpansion
For %%H In (!i!) Do EndLocal & Set "EncryptedDriveLetter[%%H]=%%G")
Set EncryptedDriveLetter[ & Pause
Notes: This must be run 'As administrator', and the last line is included just to provide some visual output. You would of course, replace that, with the rest of your script.
Just to be sure that you understand why I provided this methodology; if you were simply wanting to decrpt the encrypted drives, you don't need a for loop, or variables or manage-bde. You would just change the Get method to Call and use Decrypt.
For example:
#"%SystemRoot%\System32\wbem\WMIC.exe" /NameSpace:\\root\CIMv2\Security\MicrosoftVolumeEncryption Path Win32_EncryptableVolume Where "ConversionStatus!='0' And EncryptionMethod=!'0' And VolumeType<'2'" Call Decrypt
Just to mention, if the protection status prior to decryption was 1, i.e. Protection On, upon successful completion the protection status will be changed to 0, i.e. Protection Off.
I am trying to detect the drive letter that contains removable media (i.e. a memory stick) via wmic logicaldisk get caption^,description^,drivetype 2^>NUL then I want to use dir to get the name of the file on the drive (there should only be one but I don't know what the name is) and pass that filename into netsh wlan add profile.
I have this batch file I have written:
#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 (
SET file= | dir %%i /b
echo %%i\%file%
netsh wlan add profile filename="%%i\%file%" user=all
)
)
pause
and I expect the output to be D:\%some file%.xml but I am only getting D:. It seems that the variable %file% is not being set correctly.
I've tried many variations but I can't get it to set properly. Any suggestions welcome!
Although I do not exactly know what you are trying to accomplish, I decided to provide a modified script in which some issues are fixed. As soon as you edit your question and clarify it I will update my answer accordingly.
So here is the updated code:
#echo off
setlocal EnableDelayedExpansion
for /F "skip=1 delims=" %%L in ('
2^> nul wmic LogicalDisk ^
WHERE ^(DriveType^=2^) ^
GET Caption^,Description
') do (
for /F "tokens=1,*" %%I in ("%%L") do (
set file=myfile.xml
echo %%I\!file!
)
)
endlocal
pause
skipped header line from wmic by the skip option of for /F;
implemented a WHERE clause to the wmic command line to filter out DriveType and so to not need the if condition;
put token Description to the last position by removing DriveType, so for /F token string * can be used, as the property value may contain delimiters (spaces) on its own (although %%J is not used in the loop then);
added a second for /F loop to remove artefacts from conversion of Unicode output of wmic by first for /F;
set variable file to a constant value myfile.xml just to demonstrate delayed expansion (see also setlocal command); of course file could be set in advance outside of the loop here;
removed the pipe stuff SET file= | dir %%i /b as I have no clue what this is intended to do;
Update
After the question has been revised and clarified I can provide a solution for the requested task:
#echo off
for /F "skip=1 delims=" %%L in ('
2^> nul wmic LogicalDisk WHERE ^(DriveType^=2^) GET Caption
') do (
for /F "tokens=1" %%I in ("%%L") do (
for /F "eol=| delims=" %%F in ('dir /B "%%I\"') do (
set "FILE=%%I\%%F"
)
setlocal EnableDelayedExpansion
netsh wlan add profile filename="!FILE!" user=all
endlocal
)
)
pause
removed property Description from wmic command line as it is not used anyway;
inserted another for /F loop to capture the output of dir /B;
the built file path in FILE is passed over to netsh wlan add profile using delayed expansion, because this is required when writing and reading a variable in the same block of code; normal expansion would return the value present at the time the entire block is parsed by the command interpreter; I enabled delayed expansion inside of the loop structure in order to avoid loss of exclamation marks in the file name, because while reading for variables like %%F, delayed expansion need to be disabled to not lose such characters;
actually the interim variable FILE and so delayed expansion would not be necessary if you could guarantee that there is one file available on each drive; if this is the case, remove the setlocal and endlocal command lines, move the netsh command line into the inner-most for /F %%F loop, replace !FILE! by %%I\%%F and remove the set command line;
Im trying to make a batch script to find out a computers manufacture, model and then execute another batch script that I have already created.
So far I have this to get the Computer Manufacturer:
FOR /F "tokens=2 delims='='" %%A in ('wmic ComputerSystem Get Manufacturer /value') do SET manufacturer=%%A
and this to get the model:
FOR /F "tokens=2 delims='='" %%A in ('wmic ComputerSystem Get Model /value') do SET model=%%A
Now I'm not sure how to use the make and models as variables within an IF statement.
The first issue: how-to assign wmic output to an environment variable:
for /f "tokens=1* delims==" %%a in (
'wmic computersystem get model /value'
) do for /f "delims=" %%c in ("%%~b") do set "model=%%c"
echo %model%
Where the for loops are
%%a to retrieve the model (in the second token, %%b)
%%c to remove the ending carriage return in the value returned (wmic behaviour: each output line ends with 0x0D0D0A instead of common 0x0D0A)
Another potential issue: how-to reference an environment variable, if assigned within a command block (in parentheses):
#ECHO OFF >NUL
SETLOCAL enableextensions disabledelayedexpansion
set "model=nothing assigned yet"
for /f "tokens=1* delims==" %%a in (
'wmic computersystem get model /value'
) do for /f "delims=" %%c in ("%%~b") do (
set "model=%%c"
echo %model% :referenced using percent signs within a command block
SETLOCAL enabledelayedexpansion
echo !model! :referenced using exclamation marks within a command block
ENDLOCAL
)
echo %model% :referenced using percent signs out of a command block
ENDLOCAL
goto :eof
Run above script to see EnableDelayedExpansion effect:
==>D:\bat\StackOverflow\29893432.bat
nothing assigned yet :referenced using percent signs within a command block
System Product Name :referenced using exclamation marks within a command block
System Product Name :referenced using percent signs out of a command block
==>
Follow Stephan's comment on using If statement.
I have some text files placed in the same folder as a batch file. I need the batch file to echo the filenames without its extention line by line with a numbering. It has to be in alphabetical order. Also, it needs to store the file name into a variable fruitx where x is the number of its numbering. For example...
If I have three files in the folder:
Banana.txt
Cherry.txt
Apple.txt
I need the batch file to echo:
1) Apple
2) Banana
3) Cherry
Then, I need the variable %fruit1% be "Apple", %fruit2% be "Banana" and %fruit3% be "Cherry".
set x=0
for /F "tokens=* delims=" %%I in ('dir /b *.txt') do (
set /a x=x+1&echo %x%^) %%~nI&set fruit%x%=%%~nI)
echo %x%
Here is my code. It doesn't work and I can't figure out why. It echos 0 for each of the numberings instead.
Sorry if this sounds confusing. Thanks in advance!
When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.
This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.
To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.
In your case, the problematic variable is x, that changes its value inside the loop and needs this value inside the same loop
#echo off
setlocal enableextensions enabledelayedexpansion
set "x=0"
for /f "delims=" %%a in ('dir /a-d /b /on *.txt 2^>nul') do (
set /a "x+=1"
echo !x!^) %%~na
set "fruit!x!=%%~na"
)
echo(---------------------------
set fruit
Also, an alternative approach without delayed expansion can be to filter the generated list of files with findstr to generate the number for each element
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2 delims=:" %%a in ('dir /a-d /b /on *.txt 2^>nul ^| findstr /n "^"') do (
echo %%a^) %%~nb
set "fruit%%a=%%~nb"
)
echo(---------------------------
set fruit
#echo off
setlocal enableDelayedExpansion
set counter=0
for /f %%a in ('dir /b /a:-d /o:n *.txt') do (
set /a counter=counter+1
echo !counter! ^) %%~na
set "fruit!counter!=%%~na"
)
echo listing fruits
set fruit
I'm having trouble using wmi in a for loop. I'm simply trying to grab the first number for the OS Version to differentiate between XP (5) and Win7 (6).
The following works fine by itself for querying a single machine.
for /f "tokens=1 delims=." %%a in ('wmic /node:%machine% os get version') do #for /f "tokens=1 delims=." %%b in ("%%a") do #set osver=%%b
echo %osver%
However once I attempt to put it in a for loop, I'm only getting a blank for %osver%
for /f %%G in (list.txt) do (
for /f "skip=2 tokens=2 delims=.," %%a in ('wmic /node:%%G os get version /format:csv') do #for /f "tokens=1 delims=." %%b in ("%%a") do #set osver=%%b
echo %osver%
) > results.txt
I'm assuming it's an issue with my skip/tokens/delims but I'm not sure how to figure out where exactly it's going wrong. Any variation on them that I've tried still results in a blank line.
You also dont have to set %%b to osver, if you only need to echo the value of %%b you may as well echo %%b because on its own it is a variable that is available only to the method in which it was declared.. you only make an instance variable if you are planning on using it outside the for body ..
So you should probably stick to enabledelayedexpansion if you wana use it elsewhere ...
also you should enclose your statements with parenthesis to to avoid code mix up ....
I'd say you're assuming wrong.
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.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.
So, I'd predict that call echo %%osver%% in place of echo %osver% would fix your problem.
OTOH, echo %%b would likely be easier and quicker.
for /f %%G in (list.txt) do (
for /f "skip=2 tokens=2 delims=.," %%a in (
'wmic /node:%%G os get version /format:csv'
) do for /f "tokens=1 delims=." %%b in ("%%a") do (
set osver=%%b
echo %%b
)
CALL echo %%osver%%
) > results.txt