Batch script: echo command to file doesn't work - batch-file

I'm trying to write a script to check if the remote workstations have 64bit or 32bit operating system. The script simple should check if the directory "Program Files (x86)" exists and then echo the results to the output.txt file. For some reason or another the script doesn't even create the output.txt file. Any suggestions?
#echo off
SET output=D:\output.txt
IF EXIST "%output%" DEL "%output%"
FOR /f %%a IN (computers.txt) DO (
CALL :ping %%a
)
GOTO :EOF
:ping
SET pingtest=false
ping -n 1 %1 | find "approximate round trip time" >NUL || SET pingtest=true
set directorytest=true
set directory="\\%1%\c$\Program Files (x86)\"
IF pingtest==true (
dir %directory% >nul 2>nul
if errorlevel 1 (
set directorytest=false
)
IF directorytest==true ( echo "%1;64bit" >> "%output%" ) ELSE ( ECHO "%1;32bit" >> "%output%" )
)
IF pingtest==false (
ECHO "%1;offline" >> "%output%"
)
:EOF

There are a couple of things wrong with your code.
When assigning your %directory% variable you use \\%1%\c$\Program Files (x86). Here %1% should be %1
You are using the c$ share, which is mostly not available anymore (or at least not without administative privilidges)
It could work like this:
#echo off
setlocal enabledelayedexpansion
echo Welcome
call :ping 192.168.1.1
echo.
echo done with everything
goto eof
:ping
echo Probing %1 ...
set pingtest=false
ping -n 1 %1 | find "Approximate round trip" > nul
IF %ERRORLEVEL% == 0 (
set tdir="\\%1\c$\Program Files (x86)"
echo Looking up !tdir!
dir !tdir!
if !ERRORLEVEL! == 0 (
set arch=64bit
) else (
set arch=32bit
)
echo Architecutre of %1 is: !arch! >> output.txt
) else (
echo Remote host unreachable: %1 >> output.exe
)
goto eof
BUT:
This will return 64bit for every machine that runs the microsoft sharing service or samba and that does not share C$ to just anyone, as dir will only return a non zero ERRORLEVEL when the target does not provide that service or generally is unavailable.
On the other hand every machine that does not provide the shareing service at all, will be marked as 32bit
For any machine that does provide access to it's root drive (which none should) the script does work.
Here is a more reliably method of checking for 32/64 bit Windows OS
#echo off
set RemoteHost=192.168.1.100
set RegQry=HKLM\Hardware\Description\System\CentralProcessor\0
reg \\%RemoteHost%\%RegQry% > checkOS.txt
find /i "x86" < CheckOS.txt > StringCheck.txt
IF %ERRORLEVEL% == 0 (
echo "This is 32 Bit Operating system"
) ELSE (
echo "This is 64 Bit Operating System"
)
[source]
This one utilizes the reg.exe that can query the registry of remote Windows NT machines.
There is also the possiblity of utilizing wmic: Example

IF pingtest==true (
will never be true because the string pingtest and true are not the same.
You need
IF %pingtest%==true (
Even when you've fixed that, there's a further gotcha:
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.
Search for 'delayed expansion' on SO for many, many, many examples.

Related

Batch file to edit .ini file but do not delete blank space

I am new to StackOverflow. I want to run a batch file to find and replace a single string in an .ini file. I tried several solutions given on stackoverflow and other sites too.
A few of them are working - but delete my other lines having "space" or ";".
Here is the string that I want to find and change in my file RDConfigSettings.ini
CommunicationMode:1
I want it vice-versa:
if it is "CommunicationMode:1" then change it to "CommunicationMode:0"
if it is "CommunicationMode:0" then change it to "CommunicationMode:1"
Here is the whole content of my RDConfigSettings.ini file
;0 for Staging, 1 for Pre-Production, 2 for Production
RDEnviroment:2
;0 for disable, 1 for Enable
RDServiceLogs:0
;0 for disable, 1 for Enable
ClientValidation:0
;Validate Management Server Certificate -- 0 for Yes, 1 for No
ValidateCertificate:0
;Proxy Configuration -- 0 for Direct Internet Access, 1 for Access via Proxy
ProxyConfig:0
ProxyIP:[Proxy IP]
ProxyPort:[Proxy Port]
;0 for Https, 1 for Http
CommunicationMode:1
;Port Range Setting in below field
PortBegin:11100
PortEnd:11120
;ManagementServerURL
Registration:https://rdm.smartbioplus.com/rdm-device-app/registration
Keyrotation:https://rdm.smartbioplus.com/rdm-key-management-app/keyRotation
Telemetry:https://rdm.smartbioplus.com/rdm-telemetry-app/telemetry
Domain:rdm.smartbioplus.com
URL_Port:443
Could anyone help me? THis is my code:
#echo off
set "file=E:\CSC Softwares\MorphoRdServiceL0Soft\RDConfigSettings.ini"
:loop
findstr "^CommunicationMode:0$" "%file%" >nul || (
type "%file%"|repl "^CommunicationMode:1" "CommunicationMode:0" >"%file%.tmp"
move "%file%.tmp" "%file%" >nul
)
timeout 120 >nul
goto :loop
Moreover, it will be a great help if someone can add an Command with administrative rights that will stop a particular service "MORPHO_RD_Service" before replacing the string and then after replace the string, start the same service again.
You have code to switch from 1 to 0, but no code to switch from 0 to 1.
Below code alternates between 1 and 0 with each run of the loop.
I also changed to jrepl (more modern and powerful). It isn't necessary (though possible) to process piped data and redirect the result to another file. The /f switch gives the inputfile to process, the /o switch gives the outputfile. By giving it a single -, it uses the same filename as the input file (and overwrites it with the new(changed) data).
#echo off
set "file=t.txt"
:loop
findstr "^CommunicationMode:" "%file%" & REM this line for troubleshooting only
findstr "^CommunicationMode:0$" "%file%" >nul && (
call jrepl "CommunicationMode:0" "CommunicationMode:1" /f "%file%" /o -
) || (
call jrepl "CommunicationMode:1" "CommunicationMode:0" /f "%file%" /o -
)
timeout 1 >nul
goto :loop
Don't forget to adapt the data file name and the timeout to your needs.
Without the need for an external utility such as jrepl, which is great for some things, but not needed for such a task:
#echo off
setlocal enabledelayedexpansion
set "file=E:\CSC Softwares\MorphoRdServiceL0Soft\RDConfigSettings.ini"
for /f "tokens=1,*delims=]" %%i in ('type "%file%" ^| find /v /n "" ^& break^>"%file%"') do (
set "line=%%j"
if "!line!" == "CommunicationMode:1" (
set "line=!line:1=0!"
set "hold=!line!"
) else if "!line!" == "CommunicationMode:0" (
set "line=!line:0=1!"
set "hold=!line!"
)
echo(!line!>>"!file!"
)
echo Changed to !hold!
pause

Trying to ping a few devices using Batch arrays fails to resolve correctly

I am trying to make a batch file to quickly ping a number of devices to make sure they are all still on the network. If I type the commands in by hand, it seems give the expected output once, but doesn't seem to loop through the other iterations. Putting them in a batch file and running it somehow messes with my ability to call a variable. The IP just comes up as "i"
Is there something I'm missing? Thanks for the help.
set Device[0].Name=PC1
Set Device[0].IP=192.168.1.1
set Device[1].Name=PC2
Set Device[1].IP=192.168.1.2
set Device[2].Name=PC3
Set Device[2].IP=192.168.1.3
set _filepath=%userprofile%
echo %date% %time% > %_filepath%
SET /A i=0
:loop
IF %i%==3 GOTO END
call ping -n 1 %IDF[%i%].IP% | find "TTL=" >nul
if errorlevel 1 (
call echo %IDF[%i%].Name% IS OFFLINE >> %_filepath%
) else (
call echo %IDF[%i%].Name% Online >> %_filepath%
)
SET /A i=%i%+1
GOTO :loop
:END
In place of set /a i=0 onwards,
for /L %%s in (0,1,2) do (
CALL ping -n 1 %%Device[%%s].IP%% | find "TTL=" >nul
if errorlevel 1 (
CALL echo %%Device[%%s].Name%% IS OFFLINE >> %_filepath%
) else (
CALL echo %%Device[%%s].Name%% Online >> %_filepath%
)
)
The for /L (start,step,end) command can then be easily altered to process an extended list without using a goto
Noting that I severely suspect your setting of _filepath since it appears to be assigned a directoryname, not a filename
Also, use set "var1=data" for setting values - this avoids problems caused by trailing spaces. In comparisons, use if "thing1" == "thing2" ... to avoid problems caused by spaces in thing1/2.

Missing Operator when using set /a

So I am creating a Batch Operating System, but the problem is... IT KEEPS SAYING MISSING OPERATOR!!
Here is a snippet of my code:
#echo off
title LiME - Version 1.0
mode con: cols=86 lines=21
if exist "\SystemLiME" goto startup.jsc
) else (
if not exist "\SystemLiME" goto boot.jfk
:boot.jfk
cls
echo MAINBOOTCONFIG
echo.
echo CHECKING FOR VOLUMES TO CREATE..
ping localhost -n 5 >nul
if exist "\SystemLiME" set create-vol1=0
) else (
if not exist "\SystemLiME" set create-vol1=1
if exist "\SystemLiME\pkgs" set create-vol2=0
) else (
if not exist "SystemLiME\pkgs" set create-vol2=1
if %create-vol1% == 0 set /a count-vol-create=0
if not %create-vol1% == 0 set /a count-vol-create=%count-vol-create%+=1
if %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=0
if not %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=1
echo %count-vol-create%
pause
But what I'm focusing on is this:
:boot.jfk
cls
echo MAINBOOTCONFIG
echo.
echo CHECKING FOR VOLUMES TO CREATE..
ping localhost -n 5 >nul
if exist "\SystemLiME" set create-vol1=0
) else (
if not exist "\SystemLiME" set create-vol1=1
if exist "\SystemLiME\pkgs" set create-vol2=0
) else (
if not exist "SystemLiME\pkgs" set create-vol2=1
if %create-vol1% == 0 set /a count-vol-create=0
if not %create-vol1% == 0 set /a count-vol-create=%count-vol-create%+=1
if %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=0
if not %create-vol2% == 0 set /a count-vol-create=%count-vol-create%+=1
echo %count-vol-create%
pause
Everytime I start this code, I get 'Missing operator.'
'Missing operator.'
'ECHO is off.'
'Press any key to continue...'
please help me!! :'( (btw, im new to stackoverflow :) )
Use this batch code as replacement for second posted batch code:
:boot.jfk
cls
echo MAINBOOTCONFIG
echo/
echo CHECKING FOR VOLUMES TO CREATE..
%SystemRoot%\System32\ping.exe localhost -n 5 >nul
if exist "\SystemLiME" ( set "CreateVol1=0" ) else ( set "CreateVol1=1" )
if exist "\SystemLiME\pkgs" ( set "CreateVol2=0" ) else ( set "CreateVol2=1" )
if %CreateVol1% == 0 ( set "CountVolCreate=0" ) else ( set /A "CountVolCreate+=1" )
if not %CreateVol2% == 0 set /A CountVolCreate+=1
echo %CountVolCreate%
pause
For the reason of using echo/ instead of echo. to output an empty line see DosTips forum topic
ECHO. FAILS to give text or blank line - Instead use ECHO/
Specify in batch files executables like ping with full path and with file extension to make the batch file independent on the current values of the environment variables PATH and PATHEXT.
See the answers on IF ELSE syntax error within batch file? and batch scripting - if exist ./sdcard/file.any using adb
The string after set /A is interpreted as arithmetic expression on which each whitespace/operator delimited string is interpreted automatically as variable name whose current value should be converted to a 32-bit signed integer for evaluation of the expression. Therefore it is not needed to use immediate variable expansion using %VariableName% or delayed variable expansion using !VariableName! in an arithmetic expression because just VariableName is enough even within a command block, except the variable name contains spaces or dashes or other operators.
For that reason it is not good to use variable names containing dashes when using such variables in an arithmetic expression as the dash could be interpreted as minus operator. The usage of CamelCase naming notation is in general best for variable names in batch files. Variables with CamelCase names can be easily read and searched/replaced in batch files and are surely never mixed up with text output with echo or within a remcomment line as for example on having the following command lines in a batch file:
#echo off
rem Assign batch file name to a variable.
set "BatchFileName=%~nx0"
echo Batch file name is: %BatchFileName%
set "BatchFileName="
An arithmetic expression should contain always only one equal sign. A syntax like variable=variable+=1 is definitely not right. Right is either variable=variable+1 or shorter variable+=1.
Read the answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? for an explanation why usage of set "variable=value" is recommended for the definition (or deletion) of an environment variable. On an arithmetic expression where whitespaces are just delimiters it is usually not needed to use syntax set /A "variable=expression", but there are exceptions like the one in line 9 of batch code above.
Please note that if exist "\SystemLiME" means if there is a directory or file with name SystemLiME in root of current drive. If you want to exclude file names and check for existence of a directory append a backslash on directory path. And if you want to check for existence of the directory in current directory remove the backslash at beginning of the path.
:boot.jfk
cls
echo MAINBOOTCONFIG
echo/
echo CHECKING FOR VOLUMES TO CREATE..
%SystemRoot%\System32\ping.exe localhost -n 5 >nul
if exist "SystemLiME\" ( set "CreateVol1=0" ) else ( set "CreateVol1=1" )
if exist "SystemLiME\pkgs\" ( set "CreateVol2=0" ) else ( set "CreateVol2=1" )
if %CreateVol1% == 0 ( set "CountVolCreate=0" ) else ( set /A "CountVolCreate+=1" )
if not %CreateVol2% == 0 set /A CountVolCreate+=1
echo %CountVolCreate%
pause
This batch code checks for existence of the directories SystemLiME and SystemLiME\pkgs in current directory in comparison to above batch code which checks for existence of file or directory SystemLiME and file or directory pkgs in directory SystemLiME in root of current drive.
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.
cls /?
echo /?
if /?
pause /?
ping /?
set /?

Not defined variable carrying info from FOR LOOP batch

AIMS:
Learn how to use empty variables in a FOR LOOP routine in a batch file.
PROBLEM:
I am using whether a variable is defined or not to determine the appropriate subroutine in my script; 2 variables defined == go do something. One is defined but not the other == do something else. Both not defined == something else again.
The script checks for C$ access and the presence of a certain file within the C$ of a text file list of networked PC's. If both criteria are met by 2 variables having data set, a simple xcopy updates the file in question by going to a certain subroutine and then the LOOP moves to the next PC on the network to try and do the same.
If there is an issue I want to use the variables in an empty state to then do something else; for example report to text file that c$ was not accessible or the file was missing meaning bad install etc.
However on my 2 test machines I am breaking the folder paths to trip the error reporting routines and finding something strange I can't fix without writing more lines of code. In my text file I have 2 PC's listed a few times e.g.
PC1
PC2
PC1
PC2
PC1
PC2
PC1 has a broken file path to test error logging
PC2 All fine to test file update process
When I run the script PC1 gets reported as having a problem and logs correctly.
PC2 all is fine and the update happens fine. BUT THEN it hits PC1 again, but seems to think [even though file path still broken] that it is OKAY --as if the variable is remembered from the previous loop and of course tries to update and has problems.
Here is the code I was trying to get to work using empty variable
#echo off
color 0E
pushd %~dp0
setlocal EnableDelayedExpansion
for /f "usebackq tokens=*" %%i in ("%~dp0hostnames.txt") do (
rem Test Access Admin Shares C$
if exist "\\%%i\C$\Windows\System32" set dollar=yes
rem Test Installation Integrity
if exist "\\%%i\C$\ProgramData\config.cfg" set install=ok
echo %%i
echo !dollar!
echo !install!
pause
IF !dollar!==yes IF !install!==ok (call :updatecfg %%i)
IF !dollar!==yes IF [!install!]==[] (call :installerror %%i)
IF [!dollar!]==[] (call :errorshare %%i)
)
echo THE END
pause
exit
:updatecfg
CLS
XCOPY "%~dp0config.cfg" /Y "\\%1\C$\ProgramData" & echo %1 Update Config.cfg Succeeded! & echo %1 Update Succeeded! >>"%~dp0logpass.txt"
ping 127.0.0.1 -n 3 >nul
goto :eof
:errorshare
CLS
echo.
echo %1 Has C$ Access Issues [Logging] & echo %1 Has C$ Access Issues >>"%~dp0logfail.txt"
ping 127.0.0.1 -n 3 >nul
goto :eof
:installerror
CLS
echo.
echo %1 Cannot Find Config.cfg^!^! [Logging] & echo %1 Cannot Find Config.cfg^!^! Not Installed or Configured^? >>"%~dp0logfail.txt"
ping 127.0.0.1 -n 4 >nul
goto :eof
If I add if not exist entried to the 2 at the start and have them set something when there is a problem then this works fine. But I'd like to know if this the right way to do this or should I also be able to use empty variables. I am nearly there it's just that they are not clearing properly per loop.
Many thanks.
....
for /f "usebackq delims=" %%i in ("%~dp0hostnames.txt") do (
rem Clear variables for each iteration
set "dollar="
set "install="
rem Test Access Admin Shares C$
if exist "\\%%i\C$\Windows\System32" set "dollar=yes"
rem Test Installation Integrity
if exist "\\%%i\C$\ProgramData\config.cfg" set "install=ok"
if defined dollar (
if defined install (
call :updatecfg %%i
) else (
call :installerror %%i
)
) else (
call :errorshare %%i
)
)
....
or
....
for /f "usebackq delims=" %%i in ("%~dp0hostnames.txt") do (
rem Test Access Admin Shares C$
if exist "\\%%i\C$\Windows\System32" ( set "dollar=yes" ) else ( set "dollar=" )
rem Test Installation Integrity
if exist "\\%%i\C$\ProgramData\config.cfg" ( set "install=ok" ) else ( set "install=" )
if defined dollar (
if defined install (
call :updatecfg %%i
) else (
call :installerror %%i
)
) else (
call :errorshare %%i
)
)
....
In any case, you should ensure the variables have the adecuated value before taking a decision based on their content.

'for' loop variable not releasing on loop iterations

Been wrecking my brain all night trying to figure out why this isn't working, but one of my variables isn't releasing on the next iteration of my loop and I can't figure out why... The first pass of the loop seems to work fine, but the next iteration, the first variable gets locked and the script connects to the system that's already been configured.
I've been staring at this for a while now and no matter how I approach it, it still behaves badly. :/ The purpose is to read a text-string of a given file, and use it to modify (via Find and Replace (fnr.exe)) another file with several instances of the required data. I didn't have alot of luck with 'findstr' replacing so many instances of the text required so I went with a tool I've used before that seemed to work really well in it's previous scripting application...
Truth be told, I find myself stumbling with even the most basic code a lot of times, so any kind soul willing to impart some wisdom/assistance would be greatly appreciated!
Thanks in advance...
#ECHO ON
setlocal enabledelayedexpansion
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET lwn=
SET WKSTN=%%A
rem connect to workstation and read lwn.txt file
pushd "\\%WKSTN%\c$\"
IF ERRORLEVEL 0 (
FOR /F %%I in (\\%wkstn%\c$\support\lwn.txt) DO (
SET LWN=%%I
%~dp0fnr.exe --cl --dir "\\%WKSTN%\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF ERRORLEVEL 0 ECHO Station %LWN%,Workstation %WKSTN%,Completed Successfully >> %~dp0report.log
IF ERRORLEVEL 1 ECHO Station %LWN%,Workstation %WKSTN%, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
popd
)
)
IF ERRORLEVEL 1 (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
set wkstn=
set lwn=
echo pop d complete
)
msg %username% Script run complete...
eof
The ! notation must be used on all variables that are changed inside the loop.
C:>type looptest.bat
#ECHO OFF
setlocal enabledelayedexpansion
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET WKSTN=%%A
ECHO WKSTN is set to %WKSTN%
ECHO WKSTN is set to !WKSTN!
pushd "\\!WKSTN!\c$\"
ECHO After PUSHD, ERRORLEVEL is set to %ERRORLEVEL%
ECHO After PUSHD, ERRORLEVEL is set to !ERRORLEVEL!
IF !ERRORLEVEL! NEQ 0 (
ECHO ,,SYSTEM IS OFFLINE
) ELSE (
ECHO Host !WKSTN! is available
)
popd
)
EXIT /B 0
The workstations.txt file contained the following. (I should not give out actual host names.)
LIVEHOST1
DEADHOST1
LIVEHOST2
The output is...
C:>call looptest.bat
WKSTN is set to
WKSTN is set to LIVEHOST1
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST1 is available
WKSTN is set to
WKSTN is set to DEADHOST1
The network path was not found.
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 1
,,SYSTEM IS OFFLINE
WKSTN is set to
WKSTN is set to LIVEHOST2
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST2 is available
Although your code have several issues, the main one is the use of % instead of ! when you access the value of variables modified inside a for loop (although you already have the "enabledelayedexpansion" part in setlocal command). However, I noted that you sometimes use the FOR replaceable parameter (like in --replace "%%I") and sometimes you use the variable with the same value (%LWN%), so a simpler solution in your case would be to replace every %VAR% with its corresponding %%A for parameter.
I inserted this modification in your code besides a couple small changes that make the code simpler and clearer.
#ECHO ON
setlocal
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem Read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
rem Connect to workstation and read lwn.txt file
pushd "\\%%A\c$\"
IF NOT ERRORLEVEL 1 (
FOR /F "usebackq" %%I in ("\\%%A\c$\support\lwn.txt") DO (
%~dp0fnr.exe --cl --dir "\\%%A\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF NOT ERRORLEVEL 1 (
ECHO Station %%I,Workstation %%A,Completed Successfully >> %~dp0report.log
) ELSE (
ECHO Station %%I,Workstation %%A, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
)
)
) ELSE (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
echo pop d complete
)
msg %username% Script run complete...

Resources