I want to get some details from the Windows registry in a single line (one per key) format. But what I have tried so far gives me the details I want, but split over 2/3 lines, which makes post processing harder.
This is on Windows 10.
One option is to query the registry, so I run the following:
reg query hklm\system\currentcontrolset\enum /s /f "DeviceDesc"
This gives me output in the following format (snippet):
HKEY_LOCAL_MACHINE\system\currentcontrolset\enum\HID\VID_1A2C&PID_2124&MI_01&Col02\7&2a45f711&0&0001
DeviceDesc REG_SZ #input.inf,%hid_device_system_control%;HID-compliant system controller
HKEY_LOCAL_MACHINE\system\currentcontrolset\enum\HID\VID_1A2C&PID_2124&MI_01&Col02\8&9a82e8&0&0001
DeviceDesc REG_SZ #input.inf,%hid_device_system_control%;HID-compliant system controller
HKEY_LOCAL_MACHINE\system\currentcontrolset\enum\HID\VID_2149&PID_2117&MI_00\7&1e3fba77&0&0000
DeviceDesc REG_SZ #input.inf,%hid_device_touch_screen%;HID-compliant touch screen
HKEY_LOCAL_MACHINE\system\currentcontrolset\enum\HID\VID_2149&PID_2117&MI_01\7&316fd6b5&0&0000
DeviceDesc REG_SZ #input.inf,%hid_device_vendor_defined_range%;HID-compliant vendor-defined device
HKEY_LOCAL_MACHINE\system\currentcontrolset\enum\HID\VID_24AE&PID_2003&MI_00\8&456ad84&0&0000
DeviceDesc REG_SZ #keyboard.inf,%hid.keyboarddevice%;HID Keyboard Device
The format is:
BLANK LINE
HKEY_LOCAL.....
DeviceDesc .....
What I want is the HKEY_LOCAL... and DeviceDesc to appear on the same line of output text, so that I can use FIND/FINDSTR to get the complete info for the device I am interested in.
With the output as it stands, I cannot get the two piece of information together using DOS commands.
Is there a way to make DeviceDesc appear of the same line ?
I could write a Java/C# for this, but it seems overkill.
The command REG has no options to define the output format.
A FOR loop can be used to concatenate registry key and the device description string value for output on one line. The entire output of FOR loop can next be filtered with command FINDSTR for the device description of interest:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
(for /F "tokens=1,2*" %%A in ('%SystemRoot%\System32\reg.exe query HKLM\System\CurrentControlSet\enum /s /f "DeviceDesc"') do if /I not "%%A" == "DeviceDesc" (set "RegKey=%%A") else echo !RegKey! %%C) | %SystemRoot%\System32\findstr.exe /I /L /C:"HID-compliant touch screen"
endlocal
Please note that registry keys or description values containing one or more ! are not correct processed by this batch code because of enabled delayed environment variable expansion.
There are four spaces used to separate registry key from device description. It is of course also possible to use for example a horizontal tab.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
findstr /?
reg /?
reg query /?
set /?
setlocal /?
From what I understand, you want to combine each lines Key Path & Data. The easiest way to do this (From My knowledge) Is to grab each result of the FOR loop and use an IF statement to set the strings we will later combine.
Bellow you will find the script - In my case I just outputted the results to a document. This is fine and can be called later to with a TYPE statement inside of a FOR. However if you want to do something with these variables in the loop, just simply continue your code in-place of the ECHO [!Location! !Data!] >> Output.txt.
#ECHO OFF
#setlocal EnableDelayedExpansion
Set "RUN=0"
for /F "tokens=*" %%A in ('reg query hklm\system\currentcontrolset\enum /s /f "DeviceDesc"') DO (
Rem | Grab & organize output variables to string.
If "!RUN!"=="1" (
Rem | Second Cycle
Set "Data=%%A"
ECHO !Location! !Data! >> Output.txt
Rem | Restart Cycle
Set "Data="
Set "Location="
Set "RUN=0"
) ELSE (
Rem | First Cycle
Set "Location=%%A"
Set "RUN=1"
)
)
Goto :EOF
Related
All;
I've been all around the Internet throughout the day, trying to get a batch file written/working.
Just to start off - I am looking to make this work ONLY via a batch file and/or command line.
I'm looking to remove a specific data (not case-sensitive - as in the data could be 'data' or 'Data' or 'DATA').
Most of the OS's that I've seen the actual data to be on are Win XP machines. The specific's are as follows:
I'm looking to specifically remove the data "browser" (again, it could also be "Browser" or "BROWSER) from a REG_MULTI_SZ registry value, which the subkey is found at:
HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
The actual value is:
NullSessionPipes
I've always seen multiple pieces of data within this value (with "browser" being somewhere in the mix). Whether browser is the only piece of data in the value, or multiple pieces of data, I need to be able to execute a .bat in order to remove this.
My research points me to querying the data, removing the "browser" data, then re-inserting the remaining - I'm just not able to successfully do this.
I've attempted to just create a dummy Test key/value on my machine (Win10) at the following:
HKCU\Temp\Test
which contains the data of:
Test #1
Test #2
browser
Browser
BROWSER
Test #3
Test #4
Everything I've done, I've been unable to remove only/any form of browser from this data, leaving the rest of the "Test #x" data's.
Please advise on any solution(s) that anyone can come up with.
UPDATE
Here is the code(s) that I've been able to put together throughout my research on this (I have put together several different batch files, without positive results):
(NOTE: I in no way take credit for any of this code, as the majority of it has been compiled from/across many different locations):
Test1.bat
This test kept deleting the entire value, which I could not figure out.
#echo off
for /f "tokens=*" %%a in ('
reg query "HKCU\Temp" /v "Test" /f "browser\0Browser\0BROWSER" /d /e
^| find "REG_MULTI_SZ"
') do (
setlocal enableDelayedExpansion
rem Split
set "line=%%a"
set "value=!line:*REG_MULTI_SZ=REG_MULTI_SZ!"
call set "name=%%line:!value!=%%"
rem Trim spaces
for /L %%b in (1,1,10) do if "!name:~-1!"==" " set "name=!name:~0,-1!"
echo Deleting !name!
reg delete "HKCU\Temp" /v "!name!" /f
endlocal
)
pause
Test2.bat
This was an attempt to export the data into a txt file, then remove "browser" - another failed attempt.
#echo off
reg query HKCU\Temp /v Test > c:\Temp\tmp01.txt
FOR /F "tokens=2,3*" %%a in (c:\Temp\tmp01.txt) do call :sub1 %%b
:sub1
if %1x==browser goto end
echo %1
REG ADD HKCU\Temp /f /v Test /t REG_MULTI_SZ /d %1\0\0\0
:end
goto :eof
Test3.bat
This attempt ended up replacing all data with "%b" for my 'Test' value
#echo off & setlocal ENABLEEXTENSIONS
set k="HKCU\Temp"
set v="Test"
for /f "tokens=*" %%a in ('reg query %k% /v %v%') do (
set "d=%%b"
)
set "d=%d:browser\0=%"
set "d=%d:\0\0=%"
reg add %k% /v %v% /t REG_MULTI_SZ /d "%d%" /f
I did come across some topics where users had stated that if the data was translated to HEX/binary, that this would be easier done?
I hope this helps to give some idea to someone as to what exactly I'm doing wrong here.
As long as none of the lines inside the multi string content are doublequoted, here's how I'd probably write it:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "_k=HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
Set "_v=NullSessionPipes"
Set "_s=Browser"
Set "_d="
For /F "EOL=H Tokens=2*" %%A In ('Reg Query "%_k%" /V "%_v%"'
) Do If "%%A"=="REG_MULTI_SZ" Set "_d=%%B"
If "%_d%"=="" Exit /B
Set "_d=%_d:\0\0=%"
Set "_m="
For %%A In ("%_d:\0=","%"
) Do Echo %%A|Find /I "%_s%">Nul||Call Set "_m=%%_m%%%%A\0"
If "%_m%"=="" Exit /B
Reg Add "%_k%" /V "%_v%" /T "REG_MULTI_SZ" /D %_m:~,-2% /F>Nul
Please note that this uses Find, (line 15), to match any line containing Browser, (case insensitive), if you're wanting to match lines containing only the string Browser, (case insensitive), then you may wish to take a look at the FindStr command instead, (enter FindStr /? at the Command Prompt for usage information)
There's one major caveat: the value data for a multi string registry entry can be very long indeed, if you're sure that the string data to be written back to the registry will not exceed maximum character length then you'll be okay. If not I'm afraid I cannot think of any other simple way to perform the task. (writing the data as hex into a registry file, *.reg and import/merging it).
Here is a commented batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=2 tokens=1,2*" %%A in ('%SystemRoot%\System32\reg.exe query HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes 2^>nul') do if /I "%%A" == "NullSessionPipes" if "%%B" == "REG_MULTI_SZ" set "NullSessionPipes=%%C" & goto ProcessValue
echo Registry value NullSessionPipes was not found in registry.
goto EndBatch
rem The goal is to remove all ENTIRE strings BROWSER written in any case.
rem Other strings containing by chance also BROWSER at beginning or end
rem of the string should not be removed from multi-string value. For that
rem reason run case-insensitive string substitution with \0browser\0 to
rem really match entire BROWSER string. But there can be multiple BROWSER
rem strings in sequence which are not all removed with one run on using
rem \0browser\0 and so a loop is required make the string substitution
rem with \0browser\0 until this string is not removed anymore from value.
rem But BROWSER could be exist also as first string in value with no
rem preceding \0. For that reason \0 is extra added at beginning and
rem then removed after removing all entire BROWSER strings.
rem REG ADD requires that there is no \0\0 at end of multi-string value
rem to successfully add the multi-string value to Windows registry. The
rem data must end with last character of last string or with just \0.
rem For that reason the last two characters being \0 on Windows XP and
rem Windows Server 2003 are removed before even starting processing the
rem multi-string value. REG of Windows Vista and later Windows versions
rem does not output multi-string value with \0\0 at end like REG of
rem Windows XP. It outputs the multi-string with no \0 at end. So it
rem is necessary to append \0 at end in case of last string is BROWSER.
rem It is also necessary to do nothing if the NullSessionPipes value exists
rem with no string resulting on Windows Vista and later Windows version in
rem nothing assigned to NullSessionPipes and so the environment variable
rem does not exit at all. On Windows XP is assigned in this case just \0
rem which on further processing results also in an empty string and
rem therefore deletion of environment variable NullSessionPipes after
rem removing this final end of multi-string marker.
rem On Windows Vista and later Windows versions it is also necessary to
rem append \0 at end of environment variable string as otherwise with an
rem existing string ending by chance with a backslash this backslash would
rem be interpreted by REG as escape character for the double quote used to
rem enclose the data argument string on command line in double quotes on
rem adding the final multi-line string value. So without appending \0 at
rem end string like TEST\ as last string of NullSessionPipes value would
rem become TEST" which of course is not wanted avoided by using TEST\\0
rem at end of environment variable string for this use case.
rem After removing all entire BROWSER strings from value it is checked
rem if the new value is not identical to value read from registry which
rem means at least one BROWSER string was really removed and so it is
rem necessary to write new value without BROWSER back to Windows registry.
:ProcessValue
if "%NullSessionPipes%" == "" goto EndBatch
if "%NullSessionPipes:~-2%" == "\0" set "NullSessionPipes=%NullSessionPipes:~0,-2%"
if "%NullSessionPipes%" == "" goto EndBatch
if not "%NullSessionPipes:~-2%" == "\0" set "NullSessionPipes=%NullSessionPipes%\0"
set "NewSessionPipes=\0%NullSessionPipes%"
:RemoveBrowser
set "TmpSessionPipes=%NewSessionPipes:\0browser\0=\0%"
if not "%TmpSessionPipes%" == "%NewSessionPipes%" set "NewSessionPipes=%TmpSessionPipes%" & goto RemoveBrowser
set "NewSessionPipes=%TmpSessionPipes:~2%"
if "%NewSessionPipes%" == "%NullSessionPipes%" echo Current NullSessionPipes value does not contain the string BROWSER.& goto EndBatch
echo Current NullSessionPipes value is:
echo/
echo %NullSessionPipes%
echo/
echo New NullSessionPipes value is:
echo/
echo %NewSessionPipes%
echo/
%SystemRoot%\System32\reg.exe add HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes /t REG_MULTI_SZ /d "%NewSessionPipes%"
:EndBatch
endlocal
The output of the command line
C:\Windows\System32\reg.exe query HKLM\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters /v NullSessionPipes 2>nul
executed by FOR in a separate command process started with cmd /C in background depends on version of REG.
On Windows Vista and Windows Server 2008 and all later Windows versions the output starts with an empty line, second line is the registry key, and third line contains registry value name, value type and the value data separated by spaces.
On Windows XP and Windows Server 2003 the output starts with an empty line, next version of REG, one more empty line, fourth line contains registry key and fifth line finally contains registry value name, value type and the value data with four indent spaces and separated by a horizontal tab character.
So from different output of REG it is possible only to skip the first two lines. The next line contains the data of interest on Windows Vista/Server 2008 and all later versions of Windows. But on Windows XP and Server 2003 it is necessary to process more lines from captured REG output until fifth line is reached with the data of interest. For that reason the two additional IF conditions are used to be 100% sure that the multi-string value of registry value NullSessionPipes is really assigned to environment variable NullSessionPipes before exiting the loop and processing the value.
Output of REG on Windows XP with NullSessionPipes existing but not containing any string:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes» REG_MULTI_SZ» \0
· in output example above and all others below represents an indenting/separating space character. » represents a separating horizontal tab character.
Output of REG on Windows XP with NullSessionPipes existing but not containing any string:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes····REG_MULTI_SZ····
Example output of REG on Windows XP with NullSessionPipes containing strings:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes» REG_MULTI_SZ» browser\0test #1\0BROWSER\0Browser\0Test\#2\0TEST\\0browser\0\0
Example output of REG on Windows 7 with NullSessionPipes containing strings:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters
····NullSessionPipes····REG_MULTI_SZ····browser\0test #1\0BROWSER\0Browser\0Test\#2\0TEST\\0browser
For the example with multiple strings the data string to add on command line is: "test #1\0Test\#2\0TEST\\0"
The command line with REG to add modified value to registry does not contain option /f to force an overwrite. That gives you the possibility to check new value before really writing it to registry. Insert parameter /f left to /d if there should be no prompt to overwrite existing value once you verified that the batch file works as expected by you.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
goto /?
if /?
reg /?
reg add /?
reg query /?
set /?
setlocal /?
See also Single line with multiple commands using Windows batch file for an explanation of operator &.
#echo off
:prep
cls
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%a-%%b)
:for /l %A in (1,1,100) do copy "C:\some folder\file.ext" "C:\some folder\file-%%A.ext"
set choice=
:: test to see if directory exists
if EXIST "../delivery_%mydate%.txt" (
goto overwrite
) else (
goto start
)
:overwrite
echo.
echo delivery note already exists - continue?
set /p choice='y / n ?'
if '%choice%'=='' ECHO "%choice%" is not valid please try again
if '%choice%'=='y' goto start
if '%choice%'=='n' goto end
echo.
:start
echo.
for /l %A in (1,1,100) do copy "C:\some folder\delivery_%mydate%.ext" "C:\some folder\delivery_%mydate%.ext"
echo Choose the following:
echo 1. Directories
echo 2. Files
echo 3. quit
echo.
set /p choice=
if '%choice%'=='1' goto directory
if '%choice%'=='2' goto file
if '%choice%'=='3' goto end
cls
ECHO "%choice%" is not valid please try again
goto start
:directory
dir /ad /on /b > ../delivery_%mydate%.txt
echo.
goto checksuccess
:file
dir /a-d /on /b > ../delivery_%mydate%.txt
echo.
goto checksuccess
:checksuccess
I need to add a line of code to this batch file I have created above. I need this code to save an existing file to a higher version without deleting the previous one. This will also need to be embedded into the code I created. For example it will start saving them like: filev001, filev002, etc.
1. Some general advice for writing batch files
A list of commands is output on executing in a command prompt window help. It is advisable to use in batch files for environment variables and labels not a string which is also a command. It is possible, but not advisable.
start is a command to start an application in a separate process. So it is better to use for example Begin instead of start as label.
choice is a command for a choice which is better for single character choices than using set /P. So it is better to use for example UserChoice instead of just choice as environment variable name.
It is better to use echo/ instead echo. to output an empty line. The reason is explained by DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/.
Environment variable names and labels are easier to read on using CamelCase and can be more easily searched case-sensitive and if necessary replaced in a batch file than a name/label which can exist as word also in comments and in strings output with echo.
The answer on question Why is no string output with 'echo %var%' after using 'set var = text' on command line? explains in detail why the usage of the syntax set "Variable=string value" is recommended in batch files on assigning a string to an environment variable.
The directory separator on Windows is the backslash character \. The slash character / is the directory separator on Unix/Linux/Mac. On Windows / is used for options/parameters. The Windows kernel functions support also directory and file paths with / as directory separator by automatically correcting them to \ internally in path. But it is nevertheless recommended to use in a batch file \ in paths.
rem is the command for a comment in a batch file. :: is an invalid label and not really a comment. Lines with a label at begin are ignored for command execution. But a label cannot be used in a command block. For that reason it is recommended to use command rem because :: in a command block results often in unexpected behavior on execution of the batch file.
2. Get current date in a specific format
Let us look on the command line:
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%a-%%b)
date /t is a command which for executes in a background command process with the command line cmd.exe /C date /t for capturing the output of this command process written to standard output handle STDOUT and process the captured output line by line.
Can this be optimized?
Yes, because on running in a command prompt window set /? and reading the output help from first to last page it can be read that there is the environment variable DATE which expands to current date. So there is no need to run the command date to get current date as string.
The command date with option /t outputs the current date in the format defined for the used user account in Windows Region and Language settings. In your case it looks like the region dependent date format is MM/dd/yyyy with the weekday abbreviation at beginning (with no comma) before the date. The date format on my computer is just dd.MM.yyyy without weekday. The environment variable DATE is in same region dependent format as output of command date /t.
So the region dependent date in format ddd, MM/dd/yyyy could be also modified to yyyy-MM-dd using the command line:
for /F "tokens=2-4 delims=/, " %%a in ("%DATE%") do set "MyDate=%%c-%%a-%%b"
It is also possible to use string substitution:
set "MyDate=%DATE:~-4%-%DATE:~-10,2%-%DATE:~-7,2%"
String substitution is also explained by help output on running set /? and read the answer on
What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean?
But if yyyy-MM-dd is the wanted date format for current date independent on region settings of the used user account is advisable to use the command lines
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "MyDate=%%I"
set "MyDate=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%"
This region independent solution is really much slower than the above command lines. It is explained in detail by the answer on Why does %date% produce a different result in batch file executed as scheduled task? But it has the big advantage of being region independent.
3. Prompting user for a single character choice
The usage of set /P variable=prompt is not recommended for a single character choice because
the user can just hit RETURN or ENTER without entering anything at all resulting in variable keeping its current value or still not being defined if not defined before set /P command line;
the user can make a typing mistake and presses for example Shift+2 instead of just 2 resulting (on German keyboard) to enter " as string which most batch files using set /P breaks because of a syntax error on next command line evaluating the user input;
the user can enter anything instead of one of the characters asked for including strings which on next command line results in deletion of files and folders.
The solution is using the command choice if that is possible (depends on Windows version). choice waits for the key press of a character specified in the command options and immediately continues after one of these keys is pressed. And choice exits with the index of the pressed character in list as specified in batch file. This exit code is assigned to ERRORLEVEL which can be evaluated next also within a command block without using delayed expansion or used directly in a single goto instruction.
4. Rewritten batch file
Here is the rewritten batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem set "Folder=C:\some folder"
set "Folder=F:\Temp\Test"
:Prepare
cls
rem Get current date region independent in format yyyy-MM-dd.
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "MyDate=%%I"
set "MyDate=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%"
set "FileNumber=0"
for %%I in ("%Folder%\file-*.ext") do call :GetFileNumber "%%~nI"
goto IncrementNumber
rem Subroutine to find out highest file number without using delayed
rem environment variable expansion for number range 0 to 2147483647.
rem Numbers starting with 0 are interpreted as octal number in number
rem comparison which makes it necessary to remove leading 0 from the
rem number string get from file name starting with 5 characters.
:GetFileNumber
set "Number=%~1"
set "Number=%Number:~5%
:RemoveLeadingZero
if "%Number%" == "" goto :EOF
if "%Number:~0,1%" == "0" set "Number=%Number:~1%" & goto RemoveLeadingZero
if %Number% GTR %FileNumber% set "FileNumber=%Number%"
goto :EOF
rem Make sure the file number has at least 3 digits.
:IncrementNumber
set /A FileNumber+=1
if %FileNumber% GEQ 100 goto ExistDelivery
set "FileNumber=00%FileNumber%"
set "FileNumber=%FileNumber:~-3%"
rem Test to see if file exists already.
:ExistDelivery
if not exist "..\delivery_%MyDate%.txt" goto Begin
echo/
%SystemRoot%\System32\choice.exe /C YN /N /M "Delivery note already exists, continue (Y/N)? "
if errorlevel 2 goto :EOF
:Begin
set "FileName=file-%FileNumber%.ext"
copy "%Folder%\file.ext" "%Folder%\%FileName%" >nul
echo/
echo Choose the following:
echo/
echo 1. Directories
echo 2. Files
echo 3. Quit
echo/
%SystemRoot%\System32\choice.exe /C 123 /N /M "Your choice? "
if errorlevel 3 goto :EOF
if errorlevel 2 goto GetFileList
dir * /AD /ON /B >"..\delivery_%MyDate%.txt"
echo/
goto CheckSuccess
:GetFileList
dir * /A-D /ON /B >"..\delivery_%MyDate%.txt"
echo/
:CheckSuccess
rem More commands.
endlocal
It was not really clear for me what the entire batch code is for at all.
It would have been also easier to write the determination of highest number in a file name on knowing the possible number range like 001 to 100. So I wrote a general solution for 001, 002, ..., 099, 100, 101, ..., 1000, ..., 2147483647.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
cls /?
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
See also answer on Single line with multiple commands using Windows batch file for an explanation of & operator and read the Microsoft article about Using command redirection operators.
May be out of line here but I stumbled on a batch script here on Stack Overflow and it was just what I wanted to do. But it is a quit old post and I can't get it to work.
Original post: Batch to query registry key, find a string within that key, then create a new key
:start
setlocal ENABLEDELAYEDEXPANSION
set qry=reg query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc
for /f "Tokens=*" %%p in ('%qry%') do (
set var=%%p
set var=!var:^&=!
set var=!var:^)=!
set var=!var:^(=!
call :parse
)
endlocal
goto :EOF
:parse
if /i "%var:~0,5%" NEQ "HKEY_" goto parse1
set key=%var%
set key=%key:HKEY_LOCAL_MACHINE=HKLM%
goto :EOF
:parse1
setlocal ENABLEEXTENSIONS
for /f "Tokens=*" %%f in ('#echo %var%^|findstr /i /c:"Intel(R)"') do (
if defined key reg add %key% /v PnPCapabilities /t REG_DWORD /d 56 /f&set key=
)
endlocal >nul 2>&1
I executed this batch file on Windows 10 Home Edition and it has not changed anything in Windows registry although running it as administrator.
Can someone please help me to get this script to work?
Result of reg query:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0000
DriverDesc REG_SZ Microsoft Kernel Debug Network Adapter
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0001
DriverDesc REG_SZ Intel(R) Ethernet Connection I217-V
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0002
DriverDesc REG_SZ Qualcomm Atheros AR946x Wireless Network Adapter
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0003
DriverDesc REG_SZ Broadcom 802.11ac Network Adapter
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0004
DriverDesc REG_SZ Bluetooth Device (RFCOMM Protocol TDI)
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0005
DriverDesc REG_SZ Microsoft Wi-Fi Direct Virtual Adapter
The batch file written by Matt M does not work because all &, ( and ) are removed from all lines before calling the subroutine parse. For that reason Intel(R) is modified already to IntelR and FINDSTR cannot find in any modified line the string Intel(R). That's why reg add is never executed by batch file posted in question.
I decided to rewrite the batch code for the task to disable power management on all Intel ® network adapters.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "DetectionCount=0"
for /F "delims=" %%I in ('%SystemRoot%\System32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc 2^>nul') do call :ProcessLine "%%I"
if not %DetectionCount% == 0 echo/ & pause
endlocal
goto :EOF
:ProcessLine
set "RegistryLine=%~1"
if "%RegistryLine:~0,5%" == "HKEY_" set "RegistryKey=%~1" & goto :EOF
for /F "tokens=2*" %%A in ("%~1") do set "AdapterDescription=%%B"
if "%AdapterDescription:Intel(R)=%" == "%AdapterDescription%" goto :EOF
echo/
%SystemRoot%\System32\reg.exe add "%RegistryKey%" /v PnPCapabilities /t REG_DWORD /d 56 /f >nul
if errorlevel 1 (
echo Failed to add double word value "PnPCapabilities" with value 56 for
) else (
echo Added successfully double word value "PnPCapabilities" with value 56 for
)
echo network adapter "%AdapterDescription%" at registry key:
echo %RegistryKey%
set /A DetectionCount+=1
goto :EOF
How was the batch file tested?
I tested the script with the posted output of registry query
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc
copied into text file RegistryOutput.txt and using the FOR loop
for /F "delims=" %%I in (RegistryOutput.txt) do call :ProcessLine "%%I"
instead of the FOR loop in posted batch code processing the output of the registry query directly. The line with reg.exe add was disabled by putting echo at beginning of this line.
The output of the batch file was:
Added successfully double word value "PnPCapabilities" with value 56 for
network adapter "Intel(R) Ethernet Connection I217-V" at registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\0001
How does the batch file work?
The FOR command executes console application REG in a background command process with the command line:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" /s /v DriverDesc 2>nul
REG could output an error message to handle STDERR if it cannot find the specified registry key. This is very unlikely because this is the registry key for network adapters. However, the error message would be redirected to device NUL to suppress it using 2>nul. Please read the Microsoft article about Using Command Redirection Operators for details about input and output redirection. The redirection operator > must be escaped here with caret character ^ to be interpreted as literal character when Windows command interpreter processes the entire FOR command line before executing internal command FOR. Otherwise 2>nul would be interpreted as misplaced redirection of command FOR resulting in a syntax error message by Windows command interpreter instead of running FOR.
The output of REG written to handle STDOUT in background command process is captured by FOR and then processed line by line.
Empty lines are skipped by FOR as well as lines starting with a semicolon which does not occur here because no line ever starts with a semicolon.
All other lines would be split up into substrings (tokens) using space and horizontal tab characters as delimiters for the strings. This split behavior is not wanted here. Therefore "delims=" is used to disable line splitting and get assigned to loop variable I each non empty line.
The line with a registry key or registry value DriverDesc is passed enclosed in double quotes to subroutine ProcessLine as first and only argument. An argument string must be enclosed in double quotes if it contains 1 or more spaces or 1 or more of following characters: &()[]{}^=;!'+,`~|<>
The subroutine ProcessLine assigns first the passed line without the double quotes to environment variable RegistryLine.
A line on which the first 5 characters are case-sensitive equal to string HKEY_ is interpreted as line with a registry key assigned to environment variable RegistryKey and the subroutine is exited with goto :EOF.
Otherwise the registry line with a DriverDesc is processed as string by one more FOR loop.
The option "tokens=2*" results in splitting up the line into the three parts:
DriverDesc
REG_SZ
The string starting with first non whitespace character REG_SZ up to end of line.
The second substring (token) REG_SZ is assigned to loop variable A. This string is of no interest for this task. For that reason loop variable A is not referenced by command line executed by FOR.
The third token being the network adapter description is assigned to next loop variable after specified loop variable A according to ASCII table which is B. That is the reason why loop variables are case-sensitive while environment variables are not.
The description of the driver of the network adapter is assigned to variable AdapterDescription for further processing.
The IF condition compares the adapter description with all occurrences of Intel(R) case-insensitive replaced by an empty string and therefore removed from the description with the unmodified network adapter description. Equal strings means the network adapter description does not contain the string Intel(R) resulting in exiting the subroutine with no further processing. In other words the registry key of this network adapter is ignored by the batch file.
But the double word value PnPCapabilities is added to the registry key containing this description value with the decimal value 56 for a network adapter with Intel(R) in description most likely at beginning of the description.
All other lines of the batch file are for informing the user of the batch file if any Intel ® network adapter was detected at all and if the registry value could be successfully added to registry or if that failed, for example on not running this batch file as administrator required for write access to any key of registry hive HKEY_LOCAL_MACHINE.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
reg /?
reg add /?
reg query /?
set /?
setlocal /?
See also the Microsoft support article Testing for a Specific Error Level in Batch Files. The answer on Single line with multiple commands using Windows batch file explains operator &.
First thank you for this great site! I've learned lots of batch scripting from here, but finally got stuck. I was tasked to write a script that will go out and check a specific registry keyword and change the ones that are not correct, on all PCs on the network.
#echo off
SetLocal EnableDelayedExpansion
FOR /F %%a in (C:\batchFiles\computers.txt) DO (
FOR /F "tokens=3" %%b in (reg query "\\%%a\HKLM\SOFTWARE\some\any" /v "Forms Path") do set "var=%%b"
if "%var%" == "\\server\folder\forms\path"
echo %%a was correct
pause
if "%var%" NEQ "\\server\folder\forms\path"
echo %%a was not correct
pause
)
My boss tasked me with this not to long ago and its a little above my head, so i'm trying to learn on the fly. I tried with %errorlevel% and couldn't get it to do what I wanted either.
I had all of my PC names listed in C:\batchFiles\computers.txt. The REG_SZ key from "Forms Path" is a folder located on a network drive. Right now it says that the syntax is incorrect.
If you can understand what i'm trying to do, and have a better suggestion, I'm all ears! Oh and I'd like to output ALL of the results to a text file so I know which PCs were changed, which ones had it correct, and which ones the script couldn't reach.
Thank you so much for your time!
You enabled delayed environment variable expansion, but do not use it. %var% must be written as !var! to make use of delayed expansion as required here.
The syntax used on both if conditions is also not correct.
The registry query output by reg.exe on my computer running Windows XP is:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SOFTWARE\some\any
Forms Path REG_SZ \\server\folder\forms\path
There is first a blank line, next a line with version of reg.exe, one more blank line, a line with registry key and finally on fifth line the data of interest. Therefore I used in the batch code below skip=4 to speed it up. However, the inner loop would produce the right result also without skip=4 and therefore parsing all 5 lines.
Important is the last line. The inner loop separates by spaces. As the name of the registry value contains also a space character, the first two tokens are for Forms and Path. And the third token is REG_SZ.
The rest of the line after the spaces after REG_SZ is of real interest, but could contain also a space character. So I used in batch code below not tokens=4, but instead tokens=3* and ignored %b which holds REG_SZ. Instead %c is assigned to environment variable var resulting in getting really entire string value even if the string contains 1 or more spaces.
And the environment variable var is deleted before a new query on next computer is executed in case of a computer does not contain the registry value at all. The error message written by reg.exe to stderr is redirected to device nul for this case. The value of var would be unchanged from previous computer if not deleted before running the next query.
#echo off
setlocal EnableDelayedExpansion
for /F %%a in (C:\batchFiles\computers.txt) do (
set var=
for /F "skip=4 tokens=3*" %%b in ('%SystemRoot%\System32\reg.exe query "\\%%a\HKLM\SOFTWARE\some\any" /v "Forms Path" 2^>nul') do set "var=%%c"
if "!var!" == "\\server\folder\forms\path" (
echo %%a has correct value.
) else if "!var!" == "" (
echo %%a does not have the value at all.
) else (
echo %%a has wrong value.
)
pause
)
endlocal
Any ideas how to echo or type the last 10 lines of a txt file?
I'm running a server change log script to prompt admins to state what they're doing, so we can track changes. I'm trying to get the script to show the last 10 entries or so to give an idea of what's been happening recently. I've found a script that deals with the last line, as shown below, but can't figure out what to change in it to display the last 10 lines.
Script:
#echo off
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (c:\log09.txt) do (
set var=%%a
)
echo !var!
Example of log file:
06/02/2009, 12:22,Remote=Workstation-9,Local=,
mdb,bouncing box after updates,CAS-08754,
=================
07/02/2009, 2:38,Remote=,Local=SERVER1,
mdb,just finished ghosting c drive,CAS-08776,
=================
07/02/2009, 3:09,Remote=,Local=SERVER1,
mdb,audit of server,CAS-08776,
Any thoughts?
The script works great, just need it to pipe more lines to the screen.
Hopefully this will save Joel's eyes :)
#echo OFF
:: Get the number of lines in the file
set LINES=0
for /f "delims==" %%I in (data.txt) do (
set /a LINES=LINES+1
)
:: Print the last 10 lines (suggestion to use more courtsey of dmityugov)
set /a LINES=LINES-10
more +%LINES% < data.txt
This answer combines the best features of already existing answers, and adds a few twists.
The solution is a simple batch implementation of the tail command.
The first argument is the file name (possibly with path information - be sure to enclose in quotes if any portion of path contains spaces or other problematic characters).
The second argument is the number of lines to print.
Finally any of the standard MORE options can be appended: /E /C /P /S /Tn. (See MORE /? for more information).
Additionally the /N (no pause) option can be specified to cause the output to be printed continuosly without pausing.
The solution first uses FIND to quickly count the number of lines. The file is passed in via redirected input instead of using a filename argument in order to eliminate the printout of the filename in the FIND output.
The number of lines to skip is computed with SET /A, but then it resets the number to 0 if it is less than 0.
Finally uses MORE to print out the desired lines after skipping the unwanted lines. MORE will pause after each screen's worth of lines unless the output is redirected to a file or piped to another command. The /N option avoids the pauses by piping the MORE output to FINDSTR with a regex that matches all lines. It is important to use FINDSTR instead of FIND because FIND can truncate long lines.
:: tail.bat File Num [/N|/E|/C|/P|/S|/Tn]...
::
:: Prints the last Num lines of text file File.
::
:: The output will pause after filling the screen unless the /N option
:: is specified
::
:: The standard MORE options /E /C /P /S /Tn can be specified.
:: See MORE /? for more information
::
#echo OFF
setlocal
set file=%1
set "cnt=%~2"
shift /1
shift /1
set "options="
set "noPause="
:parseOptions
if "%~1" neq "" (
if /i "%~1" equ "/N" (set noPause=^| findstr "^") else set options=%options% %~1
shift /1
goto :parseOptions
)
for /f %%N in ('find /c /v "" ^<%file%') do set skip=%%N
set /a "skip-=%cnt%"
if %skip% lss 0 set skip=0
more +%skip% %options% %file% %noPause%
You should probably just find a good implementation of tail. But if you really really insist on using CMD batch files and want to run on any NT machine unmolested, this will work:
#echo off
setLocal EnableDelayedExpansion
for /f "tokens=* delims= " %%a in (c:\tmp\foo.txt) do (
set var9=!var8!
set var8=!var7!
set var7=!var6!
set var6=!var5!
set var5=!var4!
set var4=!var3!
set var3=!var2!
set var2=!var1!
set var1=!var!
set var=%%a
)
echo !var9!
echo !var8!
echo !var7!
echo !var6!
echo !var5!
echo !var4!
echo !var3!
echo !var2!
echo !var1!
echo !var!
There are several windows implementations of the tail command. It should be exactly what you want.
This one sounds particularly good:
http://malektips.com/xp_dos_0001.html
They range from real-time monitoring to the last x lines of the file.
Edit: I noticed that the included link is to a package It should work, but here are some more versions:
http://www.lostinthebox.com/viewtopic.php?f=5&t=3801
http://sourceforge.net/projects/tailforwin32
If file is too large it can take too long to get count of lines
another way is to use find and pass it a nowhere string
$find /v /c "%%$%!" yourtextfile.txt
this would result an output like this
$---------- yourtextfile.txt: 140
then you can parse output using for like this
$for /f "tokens=3" %i in ('find /v /c "%%$%!" tt.txt') do set countoflines=%i
then you can substract ten lines from the total lines
After trying all of the answers I found on this page none of them worked on my file with 15539 lines.
However I found the answer here to work great. Copied into this post for convenience.
#echo off
for /f %%i in ('find /v /c "" ^< C:\path\to\textfile.txt') do set /a lines=%%i
set /a startLine=%lines% - 10
more /e +%startLine% C:\path\to\textfile.txt
This code will print the last 10 lines in the "C:\path\to\textfile.txt" file.
Credit goes to OP #Peter Mortensen
using a single powershell command:
powershell -nologo "& "Get-Content -Path c:\logFile.log -Tail 10"
applies to powershell 3.0 and newer
I agree with "You should use TAIL" answer. But it does not come by default on Windows. I suggest you download the "Windows 2003 Resource Kit" It works on XP/W2003 and more.
If you don't want to install on your server, you can install the resource kit on another machine and copy only TAIL.EXE to your server. Usage is sooooo much easier.
C:\> TAIL -10 myfile.txt
Here's a utility written in pure batch that can show a lines of file within a given range.To show the last lines use (here the script is named tailhead.bat):
call tailhead.bat -file "file.txt" -begin -10
Any ideas how to echo or type the last
10 lines of a txt file?
The following 3-liner script will list the last n lines from input file. n and file name/path are passed as input arguments.
# Script last.txt
var str file, content ; var int n, count
cat $file > $content ; set count = { len -e $content } - $n
stex -e ("["+makestr(int($count))) $content
The script is in biterscripting. To use, download biterscripting from http://www.biterscripting.com , save this script as C:\Scripts\last.txt, start biterscripting, enter the following command.
script last.txt file("c:\log09.txt") n(10)
The above will list last 10 lines from file c:\log09.txt. To list last 20 lines from the same file, use the following command.
script last.txt file("c:\log09.txt") n(20)
To list last 30 lines from a different file C:\folder1\somefile.log, use the following command.
script last.txt file("C:\folder1\somefile.log") n(30)
I wrote the script in a fairly generic way, so it can be used in various ways. Feel free to translate into another scripting/batch language.