Search code examples
batch-filevariablesfor-loopregistry

remote registry query problems


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 :)


Solution

  • 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:

    1. Simply delete the errortype handling
    2. 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