remote registry query problems - batch-file

Checking for XP via the registry using this script in batch.
#echo off
pushd %~dp0
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && set OS=XP || set OS=NEWER
echo %os%
pause
)
in pxhost I have the list of pc's to check for xp and then do something, or not.
However I cant get the above to work. The variable is never set and it just echos back "windows_NT". If, however, I take the for loop out and run the reg query without the variable:-
reg query "\\xp-4c54fa50d0da\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && set OS=XP || set OS=NEWER
the echo works and reports "XP", or "newer" if I change "5.1" to "5.7" for test purposes.
What's going on? Thanks.
thanks for answers but now I have more problems
From here I can now echo the right responses but the calling doesn't work at all. I have an XP, win 7 32 and win 10 64 in test in the text file pxhosts.
XP is first in the list and gets ignored even when it is echo'd back correctly. something is stopping the calling from happening. Really driving me mental this lol.
I am trying to write a remote permissions script that applies to either XP filr system or 32 or 64 (newer windows). The total code is below:-
#echo off
setlocal enabledelayedexpansion
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && set pctype=XP || set pctype=NEWER
cls
echo !pctype!
pause
if !pctype! == !XP! ( call :XP !%%A!)
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set arc=32 || set arc=64
cls
echo !arc!
pause
if !arc! == !64! ( call :64 !%%A!)
if !arc! == !32! ( call :32 !%%A!)
)
:32
echo 32 okay
pause
icacls "\\%1\C$\ProgramData\folderFoo" /T /C /grant(:r) "Domain Users":(OI)(CI)(F) /inheritance:e >> "%~dp0%1.txt" 2>&1
pause
rem return from a subroutine
exit /B
:64
echo 64 okay
pause
icacls "\\%1\C$\Program Files (x86)\Folderfoo" /T /C /grant(:r) "Domain Users":(OI)(CI)(F) /inheritance:e >> "%~dp0%1.txt" 2>&1
pause
rem return from a subroutine
exit /B
:XP
echo xp okay
pause
CACLS "\\%1\C$\Documents and Settings\All Users\Application Data\Folderfoo" /E /T /C /G "Domain Users":F >> "%~dp0%1.txt" 2>&1
pause
rem return from a subroutine
exit /B
new edit 201216
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL
IF %ERRORLEVEL%==0 set pctype=XP
IF %ERRORLEVEL%==1 set pctype=NO
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL
IF %ERRORLEVEL%==0 set arc=32
IF %ERRORLEVEL%==1 set arc=64
IF !pctype!==XP (call :xp %%A)
IF !pctype!==NO IF !arc!==32 (call :32 %%A)
IF !arc!==64 (call :64 %%A)
)
This is now picking up the 64 or 32 using reg query, however because an XP machine will always be a 32 bit machine it keeps running the code for newer windows PC with 32 bit as well. My subroutines for XP are dealing with docuemnts and settings folder, newer 32 machines just the user folder format.
I need to run a subroutine on XP machines, 32 bit newer machines and 64 bit machines. At the moment xp code runs on 32 bit machines and vice versa because they share 32 bit architecture.
I have tried above to eradicate the problem by only running 32 bit routine if the pc is newer with a 32 bit file structure by way of variables (2nd line of IF's). Thanks :)

If the thingy works without the for-loop and the variable is not set within a for-loop consider a search for something like Batch variable not set in for-loop and boom there will be a plenty of questions like yours.
Answer is the same for all:
Use Delayed Expansion!
To use that, add the line setlocal EnableDelayedExpansion to the beginning of the batch-file and whereever you need a variable in a closed set of parenthesis like a for-loop or an if-condition change %myVar% to !myVar!.
Example to verify:
#echo off
setlocal EnableDelayedExpansion
set foo=foo
if 1==1 (
set foo=bar
echo Not delayed: %foo%
echo Delayed: !foo!
) ELSE (
echo If you land here something went heavily wrong...
)
Reasoning:
In batch closed sets of parenthesis are calculated when the beginning is reached. In the above example that means that when the program reaches if 1==1 it knows the value of foo as "foo" eventhough I changed it seemingly before.
An alternative is to use
call echo %%myVar%%
In your example however you would not even need that variable... In the same positions where you set the value of OS you could as well simply echo it:
... && set OS=XP || set OS=NEWER -> ... && echo XP || echo NEWER
Edit after big question edit:
You only and really only need exclamation marks when accessing a variable that got set within the loop! You made two major mistakes I spotted so far:
1) For-Loop variables are set in the loops header and with that are an exception from this rule: %%A is the correct way of accessing it and not !%%A!
2) If you want to make a string comparison like if varValue == thisString you should NOT surround the string to check with exclamation marks:
if !arc!==64 should do the trick here!
Else the comparison would look like if 64==!64! which is not the desired behaviour I guess.
Read your questions edit closer again:
Something is stopping the calling from happening
Exactly the problem with the if described above in point 2 :)
Edit after question for another method to find the queried values remotely:
After a short search and playing around a bit with the results I came up with this (Windows 7+ required on executing machine; tested with Windows 10 Pro):
#echo off
setlocal EnableDelayedExpansion
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
for /f "tokens=2 delims=:" %%B in ('systeminfo /s "%%~A" ^| findstr /i "Systemtype"') do (
call :removeLeadingSpaces "%%~B" type
echo !type!
)
for /f "tokens=2 delims=:" %%C in ('systeminfo /s "%%~A" ^| findstr /i "Operatingsystemname"') do (
call :removeLeadingSpaces "%%~C" osName
echo !osName!
)
)
pause
Goto:eof
:removeLeadingSpaces
for /f "tokens=* delims= " %%f in ("%~1") do set %2=%%f
Explanation:
Reads the computernames from the file as it already does. Takes the second part from the output of the command systeminfo /s <computername> queried for the specific strings "Systemtype" and "Operatingsystemname". The ^ is there to escape the piping symbol |. It then sends the string to a subfunction that truncates the leading spaces and sets them to the specified variable.
NOTE: The strings used to query may vary for a different language setting! Those above are freely translated from my German OS! To check how they are for your language setting go ahead and open a commandprompt and type systeminfo and hit Enter Now look for the ones you need.
Systemtype outputs the processor architecture and OSname the name of the operating system (who would have thought that ey?). If you have found what you need change the strings from my example to your needs.
From where it says echo !type! and echo !osName! do whatever you want with those variables. Any questions left? Feel free to ask!
Edit after chat conversation
Your problem (I think) is here:
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set arc=32 || set arc=64
IF ERRORLEVEL==0 set arc=32
IF ERRORLEVEL==1 set arc=64
When using the way of processing the outcome of the find directly using && echo positive result || echo negative result the errorlevel will always reside at 0! So you got two ways of handling this:
Simply delete the errortype handling
Delete the part && set arc=32 || set arc=64 so the errorlevel is set accordingly to the outcome of the command as it is already done at the check for XP
You can test this on your own in the commandline with the queries from your question:
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL
echo %ERRORLEVEL%
This should echo 1 if you do not have an XP-computer (what I hope for you ;) ). This is because find could not find the string specified -> falsey value.
Now reset the errorlevel:
set ERRORLEVEL=0
And try this one:
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && echo should be 0 || echo should be 1
Would be the version without direct errorlevel handling but with acting according to the direct result of the find command.
Now do echo %ERRORLEVEL% You will notice that it is 0 eventhough the above command should have returned a falsey value.
Edit after finding the mistake in chat:
In the end it was one lethal space too much... ONE SPACE screwing up parts of the script.
Base script:
#echo off
setlocal EnableDelayedExpansion
pushd %~dp0
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL
IF !ERRORLEVEL!==0 set pctype=XP
IF !ERRORLEVEL!==1 set pctype=NOXP
CLS
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL
IF !ERRORLEVEL!==0 set arc=fs1
IF !ERRORLEVEL!==1 set arc=fs2
CLS
IF !pctype!==XP (call :XPFS %%A)
IF !pctype!==NOXP IF !arc!==fs1 (call :type1 %%A)
IF !arc!==fs2 (call :type2 %%A)
)
Goto :eof
:XPFS
REM Do things for XP computer
Goto :eof
:type1
REM Do things for 32-bit computer
Goto :eof
:type2
REM Do things for 64-bit computer
Goto :eof

Related

Native cmd tail command for windows

I am looking to build a tail to read log files on windows using only native cmd.
There are various ways to to read a file:
type file.txt
more file.txt
They however do not have a default option to read updates from a file, so does not represent anything like tail.
With a few little hacks and using more to read the file, skipping what was already read before, we are able to tail the file, "almost" realtime.
#echo off & set cnt=0
if "%~1" == "" echo no file specified, usage: "tail.cmd <filename>" & goto :eof
if not exist "%~1" echo file "%~1" does not exist & goto :eof
:tail_sub
2>nul (>>"%~1" echo off) && (goto :file) || (goto :tail_sub)
:file
for /f "tokens=1*delims=]" %%i in ('more "%~1" +%cnt% ^| find /v /n ""') do (
set "line=%%j"
set /a cnt+=1
call echo(%%line%%
)
goto :tail_sub
Currently, without adding a timeout to slow down the infinite loop, it consumes around 5.6MB memory, which is very acceptable in my view.
This does not yet take care of all special characters, like |<>&, but I will spend some time on this to cater for all scenarios I can think off.

how to for loop and check if a number of file is present in the folder .bat

I am looping trough a folder. I want to understand if there are 20 zip files. If these 20 zip files are present then another batch will call. Otherwise I need to build a powershell script to send an email to somebody as error message
setlocal enableextensions enabledelayedexpansion
set /a count = 1
for /f %%a in (\\ITWS2162\work\SCM\RawData\SSC_Proj\*.zip) do (
set /a count += 1
set "TRUE="
if %%~xa=.zip && %%~xa == 20 set TRUE = 1
ECHO "there are 20 files Matrioska Process starts"
IF defined TRUE (
C:\Users\RefosLeo\Desktop\Python\motherbatch.bat
) ELSE (
EXIT
)
)
Please tell me what is wrong because, actually this code does not work
untested
You can use find to count the number of files and just use the count directly:
for /f %%a in ('dir /b \\ITWS2162\work\SCM\RawData\SSC_Proj\*.zip ^| find /c /v ""') do (
if /i %%a EQU 20 (
rem do what you want here when it equals 20
C:\Users\RefosLeo\Desktop\Python\motherbatch.bat
)
)
This finds exactly 20 zip files. You could change EQU to GEQ if you actually want to find 20 or more .zip files.
Though you might want the inverse -- exit when less than or not equal to 20:
for /f %%a in ('dir /b \\ITWS2162\work\SCM\RawData\SSC_Proj\*.zip ^| find /c /v ""') do (
if /i %%a LSS 20 exit
)
rem continue here with the rest of your script
C:\Users\RefosLeo\Desktop\Python\motherbatch.bat
That is anything less than 20. If you want to exit when not equal (so less than or greater than 20) you would use if /i %%a NEQ 20 exit
If you want to continue with the method you started with, I think the following might be what you were going for:
setlocal enableextensions enabledelayedexpansion
set /a count = 0
for /f %%a in (\\ITWS2162\work\SCM\RawData\SSC_Proj\*.zip) do (
set /a count += 1
if /i !count! EQU 20 (
ECHO "there are 20 files Matrioska Process starts"
C:\Users\RefosLeo\Desktop\Python\motherbatch.bat
)
)
if /i %count% LSS 20 exit
You can't put the exit inside the for loop because you'll never know until the for loop is complete that you didn't reach 20 zip files.
There isn't actually any need for a for-loop, delayed expansion, or setting any variables, to perform your task. Here therefore is a method which uses xcopy and findstr.
#"%__AppDir__%xcopy.exe" "\\ITWS2162\work\SCM\RawData\SSC_Proj\*.zip" . /LQ^
| "%__AppDir__%findstr.exe" "\<20\>" 1> NUL && (
"C:\Users\RefosLeo\Desktop\Python\motherbatch.bat") || (Exit /B)
The above has a flaw, in that it counts any files with extensions beginning with .zip as opposed to those which are exactly zip. If this is an issue to you, you could try using where.exe and find instead:
#"%__AppDir__%where.exe" "\\ITWS2162\work\SCM\RawData\SSC_Proj":"*.zip" 2> NUL^
| "%__AppDir__%find.exe" /V /N "" | "%__AppDir__%find.exe" "[20]" 1> NUL && (
"C:\Users\RefosLeo\Desktop\Python\motherbatch.bat") || (Exit /B)
The examples above are both single line commands, split over several lines for readability. I have left the parenthesized command (Exit /B) as you had, because you'll likely want to replace Exit /B with your command(s) to build and send the email. You may, depending upon your specific circumstances, wish to Call motherbatch.bat instead.

Output multiple registry values per key

I want to output both DisplayName and DisplayVersion of each program installed.
for /f "tokens=2*" %a in (
'reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s ^
| findstr /c:"DisplayName" /c:"DisplayVersion"'
) do #echo %b
It does output both of them one per line but I want to display them on one line, how would you do that?
> DisplayName, DisplayVersion
You probably should check both 32bit and 64bit registries. If I don't specify which one, then my REG QUERY searches only 64bit by default.
Not all program keys have DisplayName and/or DisplayVersion.
The code below lists the full key if DisplayName is not present, and lists an empty version if DisplayVersion is not present. Both 32bit and 64bit registries are searched.
#echo off
setlocal enableDelayedExpansion
set "key="
set "name="
set "ver="
for %%s in (32 64) do (
for /f "delims=" %%A in ('reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s /reg:%%s 2^>nul') do (
set "ln=%%A"
if "!ln:~0,4!" equ "HKEY" (
if defined name (echo "!name!","!ver!") else if defined key echo "!key!","!ver!"
set "name="
set "ver="
set "key=%%A"
) else for /f "tokens=1,2*" %%A in ("!ln!") do (
if "%%A" equ "DisplayName" set "name=%%C"
if "%%A" equ "DisplayVersion" set "ver=%%C"
)
)
)
if defined name (echo "!name!","!ver!") else if defined key echo "!key!","!ver!"
The easiest method would be to just output the information directly within Windows PowerShell but that would be directly contrary to the tags you've applied to this question.
Here therefore is a batch file which uses Powershell:
#Echo Off
Set "KP=Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
PowerShell -C "GP HKLM:\%KP%\*|Select DisplayName,DisplayVersion|FT -A -H"
Pause
You may remove \Wow6432Node from line 2 if you're not using this on a 64bit Operating System.
It is possible for the DisplayName output to be truncated due to their character lengths and cmd.exe's buffersize. This can be worked around using the following, (possibly crude), code:
#Echo Off
Set "KP=Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
Set/A W=90,H=120
PowerShell -C "&{$H=Get-Host;$R=$H.UI.RawUI;$B=$R.BufferSize;"^
"$W=$R.WindowSize;$B.Width=If (%W% -GT $W.Width) {%W%} Else {$W.Width};"^
"$B.Height=If (%H% -GT $W.Height) {%H%} Else {$W.height};$R.BufferSize=$B};"^
"GP HKLM:\%KP%\*|Select DisplayName,DisplayVersion|FT -A -H"
Pause
In the above code you can adjust that height/width on line 3 as required, this may be necessary if you have some very long DisplayName's or a huge list of installed software under that key.

Pinging Multiple Computers IF Statement

I'm trying to create a little batch file that checks multiple PCs read from a text file. For any PCs it finds are pingable, it writes a line in a "results" text file saying so. Here's what I've got:
#Echo off
set file=C:\logs\registercheck.txt
date /t >%file%
FOR /F %%I IN (C:\work\regnames.txt) DO (ping /n 1 %%I | ping /n 1 %%I | IF errorlevel 1 goto :nextreg | echo %%I is still on and has not been powered off! >>%file% | :nextreg)
PAUSE
So...when I run the file, I get multiple lines of "goto was unexpected at this time" and the only thing written in my output text file is the date. What am I doing wrong?
Thank you!
#Echo off
setlocal enableextensions disabledelayedexpansion
set "logFile=C:\logs\registercheck.txt"
set "inputFile=C:\work\regnames.txt"
>>"%logFile%" date /t
for /f "usebackq delims=" %%i in ("%inputFile%") do (
ping -n 1 %%i >nul 2>nul
if not errorlevel 1 (
>>"%logFile%" echo(%%i is still on and has not been powered off!
)
)
You have two errors.
The first is that to put all the commands in a single line, the separator is not the pipe character (|) but the ampersand (&)
The second is that inside the do code block of the for command, if one goto is executed, the for command is finished, independently of where the label is placed. And labels inside for code blocks usually generate errors (depends of its position).
If instead of the previous code, you want a single line loop, it can be written as
for /f "usebackq delims=" %%i in ("%inputFile%") do ( ping -n 1 %%i >nul 2>nul & if not errorlevel 1 >>"%logFile%" echo(%%i is still on and has not been powered off! )
or
for /f "usebackq delims=" %%i in ("%inputFile%") do ( ping -n 1 %%i >nul 2>nul && >>"%logFile%" echo(%%i is still on and has not been powered off! )
that makes use of the && construct. It is intended as a shortcut for the if not errorlevel 1 .... If the command at the left of the && does not raise an errorlevel, then the command on the right side is executed.
This for the batch sintax. Now the ping. There is a difference in how ping command behaves depending of the ip version. It is not the same to ping an ipv4 address than to ping an ipv6 address. If needed you can grab from here a subrotine to handle the differences.

How do I make a batch variable = to a command answer?

I have looked in quite a few places. Maybe I have not asked the right question, but I am now here. I am making a batch file to sense what the Operating system is.
My current attempt:
set os=systeminfo |find "OS Name"
EDIT: a WINDOWS batch file
EDIT: If a command returns a line in the console. How do I make a variable = the returned string. (This is my main question. Sorry if I was not very clear.)
Example:
varName = Command |find "String"
#echo off
for /f "delims=" %%a in ('wmic OS get caption ^|find /i "windows"') do (set #OS=%%a)
set #
See how this works for you:
#echo off
setlocal EnableDelayedExpansion
:: Routine by Aacini
::Identify OS
for /F "delims=" %%a in ('ver') do set ver=%%a
set Version=
for %%a in (95=95 98=98 ME=ME NT=NT 2000=2000 5.1.=XP 5.2.=2003 6.0.=Vista 6.1.=7 6.2.=8 6.3=8.1) do (
if "!Version!" equ "this" (
set Version=Windows %%a
) else if "!ver: %%a=!" neq "%ver%" (
set Version=this
)
)
::Identify bit
if exist "%SYSTEMDRIVE%\Program Files (x86)" (
set Type=64 bit
) else (
set Type=32 bit
)
::Display result
echo %Version% %Type%
echo/
pause
this
http://www.robvanderwoude.com/sourcecode.php?src=winver2_nt
and this
http://www.robvanderwoude.com/sourcecode.php?src=winver_bat
should do the work.Not sure if are updated to support Windows 8.
Here are all build numbers.
EDIT (only checks if it is Windows XP or 7 - see comments bellow.Will work also on Windows home editions which have no WMIC command)
#echo off
for /f " tokens=4,5 delims=. " %%a in ('ver') do set /a wver=%%a%%b
if "%wver%" == "61" echo Windows7
if "%wver%" == "51" echo WindowsXP
In bash:
UN=`uname`
echo $UN
In windows you can just examine the OS environment variable:
echo %OS%
Maybe this is what you are looking for:
wmic os get caption /value
There isa lot more of information. Try
wmic os get /value
Here is a routine I wrote to do this. WMIC returns goofy names for some OS's and version number isn't robust enough since some versions are the same for server and workstation. This works for everything.
#echo off
setlocal
call :GetOS cap bit sp
echo %cap%%bit% (%sp%)
exit /b
:GetOS caption servicepack
setlocal
set arc=%PROCESSOR_ARCHITECTURE%
set key="HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
for /f "tokens=3*" %%a in (
'reg query %key%^|findstr /i ProductName') do set cap=%%a %%b
for /f "tokens=3*" %%a in (
'reg query %key%^|findstr /i CSDVersion') do set sp=%%a %%b
endlocal & set %1=%cap% & set %2=%arc% & set %3=%sp%
exit /b

Resources