Editing and reading a .txt file with batch commands - batch-file

I'm trying to create a batch file for an automated testing script.
Everytime the batch for the master script is started up, it should open an existing .txt-file in the same directory containing something like this:
10.200.6.111 inactive
10.200.6.112 inactive
10.200.6.113 inactive
10.200.6.114 inactive
etc...
It should then navigate to the line with it's own IP (which is specified in the batch) and replace the 'inactive' tag with 'active' (indicating that this system has now started testing). Ideally it would also append a timestamp, maybe with something along these lines:
for /f "tokens=1-5 delims=:" %%d in ("%time%") //and then add %%d-%%e at the end?
I've tried to do something similar before and also looked through existing topics but everything seems to be very specific to one situation and I lack the skills to adapt them myself. Ultimately I would need a different .bat to also read from this file and delay any action until all 'active' tabs are gone. But since as of now there is only one vm doing the testing, it would really be a weight off my shoulder if I could just get this to work. Thanks in advance for any help and I apologize if this is a duplicate, I really did try to find something I could use!
PS: I'm really terrible at 'getting' generic code sometimes, it's perfectly possible this has been clearly answered before and I'm just not capable of understanding the solution.
Edit: Just to clarify, it should look roughly like this:
10.200.6.111 inactive
10.200.6.112 inactive
10.200.6.113 active 14:20
10.200.6.114 inactive
etc...
Edit 2: Actually, after thinking about it some more, I've concluded that I can probably do it better by just using my scripts and keeping the batch files simple.

#echo off
setlocal enableextensions disabledelayedexpansion
:: define the log file
set "file=%~nx0.test.txt"
:: generate some lines in log file to test
if not exist "%file%" (
echo 10.200.6.111 inactive
echo 10.200.6.112 inactive
echo 10.200.6.113 inactive
echo 10.200.6.114 inactive
) > "%file%"
:: define the ip of the current node
set "ip=10.200.6.114"
:: change status in log file to active and shows sucess/failure of operation
call :changeStatus "%file%" "%ip%" "active %time:~0,5%"
if errorlevel 1 (
echo failed to change status
) else (
echo status changed
)
:: dump file to see changes
call :dumpFile "%file%"
:: change status in log file to inactive
call :changeStatus "%file%" "%ip%" "inactive"
:: dump file to see changes
call :dumpFile "%file%"
exit /B
:dumpFile file
echo(
echo(----------------------------------
type "%~1"
echo(----------------------------------
echo(
exit /b
:changeStatus file ip status
setlocal enableextensions
set "file=%~1"
set "ip=%~2"
set "status=%~3"
set "retries=0123456789"
set "exitCode=0"
:changeStatusRetry
(>"%file%.lock" (
set "reset=1"
for /f "usebackq tokens=1*" %%a in ("%file%") do (
if defined reset ( set "reset=" & >"%file%" break )
(if "%%a"=="%ip%" (
echo(%%a %status%
) else (
echo(%%a %%b
)) >> "%file%"
)
) && set "exitCode=0" || set "exitCode=1") >nul 2>nul
if "%exitCode%"=="1" (
ping -n 2 localhost >nul 2>nul
set "retries=%retries:~0,-1%"
if defined retries ( set "exitCode=0" & goto changeStatusRetry )
)
del /q "%file%.lock" >nul 2>nul
endlocal & exit /b %exitCode%
This uses a subroutine to handle the changes to log file. It also handles locking on the file from multiple processes. Log file access is locked while changing data and if at the time of changing the file it is locked, it retries up to 10 times to write changed data. On return from the subroutine, errorlevel indicates the result of the operation.

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

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.

batch - is there a way to synchronize-lock txt file in batch?

I need to create a batch file that trace out using tracert command some ip, and write the trace to txt files. I want it to be fast so I want to start for each trace a new command to make all the trace request start at once.
there is my ping.bat:
#echo off
set saveUnrechableLocation=..\pingUnreachableInfo\pingUnrechableInfoDB.txt
set IpListLocation=..\ipInfo\all_DB_ip.txt
set reachableLocation=..\pingRechableInfo\RechableIp\pingRechableInfoDB.txt
set trace=..\pingRechableInfo\tracert\tracertDB.txt
set numberOfPings=1
#echo pinging DB > %saveUnrechableLocation%
copy /y NUL %reachableLocation% > NUL
copy /y NUL %trace% > NUL
for /F "tokens=*" %%A in (%IpListLocation%) do (
ping -n %numberOfPings% %%A | find "TTL=" >nul
if errorlevel %numberOfPings% (
#echo %%A not rechable >> %saveUnrechableLocation%
)
if not errorlevel %numberOfPings% (
#echo %%A >> %reachableLocation%
start trace.bat %trace% %%A
)
)
and the trace.bat look like that:
#echo off
set saveLocation=%~1
set ip=%~2
tracert %ip% >> %saveLocation%
exit
the problem is that when I'm trying to use this I'm getting this problem:
the process cannot access the file because it is being used by another process
what can I do to resolve this problem? thanks!
Windows redirection does not allow multiple processes to have the same file open for write access at the same time. The write operations must be serialized. This can be done with batch processing, as demonstrated at https://stackoverflow.com/a/9344547/1012053. However, I don't think that solution will help in your case.
Each tracert process takes considerable time, and the output must be redirected the entire time. But you want multiple processes running at the same time, all output redirected to the same file. Even if you were to make it work, the output would be interleaved, and you wouldn't be able to figure out what it all means.
I recommend redirecting each tracert output to a unique file. You could incorporate the ip address into the output file name, you could use the technique I showed to merge the files after each process completes.
Note there is no need to pass the output location. Each child process has access to the trace variable, so it can easily redirect to the correct location.
outline of ping.bat changes
...
set trace=..\pingRechableInfo\tracert\tracertDB
...
start trace.bat %%A
...
modified trace.bat
#echo off
tracert %1 >%trace%_%1.txt %= Redirect TRACERT to unique temp file =%
:merge
2>nul ( %= Hide error messages inside the outer parentheses =%
>>%trace%.txt ( %= Attempt stdout redirection - Fails if already locked =%
type %trace%_%1.txt %= Write the temp file to the merge file =%
(call ) %= Clear any error that TYPE may have generated =%
)
) || goto :merge %= Loop back and try again if stdout redirection failed =%
del %trace%_%1.txt %= Delete the temporary file =%
exit
A shortened form without comments could look like:
#echo off
tracert %1 >%trace%_%1.txt
:merge
2>nul (>>%trace%.txt (type %trace%_%1.txt&(call )))||goto :merge
del %trace%_%1.txt
exit
this is fixed code based on dbenham answer:
#echo off
tracert %1 >%trace%_%1.txt
:merge
2>nul (
>>%trace%.txt (
type %trace%_%1.txt
(call )
)
) ||goto :merge
del %trace%_%1.txt
exit

Batch file with "menu" for deleting files referencing path from a text file

Good Day to All,
Please pardon me if my question may appear odd. I was not able to pinpoint past examples that suit the ideal script that i wish to accomplish.
Currently, i have the following script. Essentially is to delete files from folders whose location is listed in a text file (del_location.txt).
For /F "tokens=*" %%A in (del_location.txt) do del \Q "%%A"
I did not choose to use Forfiles, as the network i am working on in my company is at Windows 2000 without the ability to run Forfiles and due to security reasons, are not able to install any patches.
There are multiple location in del_location.txt and there are instances that my users may not wish to delete all the listed location. Hence i am wondering how do i write a "menu" function, that reads off the listed location in del_location.txt and ECHO it as a selection. Lets the user choose the selection in a Yes/No fashion before the commencement of the delete sequence? Thus allowing the user to "walk off" from the terminal after choosing what he wants to delete.
*Also, from as far as i can find online, without Forfiles, i would not be able to choose to delete only files that are older than N days. Am i right to state so?
Your help and advices are greatly appreciated.
You can set each lines in del_location.txt to an array. Since batch don't support array, then we need to do it manually.
#echo off
setlocal enabledelayedexpansion
:main
set lines=0
For /F "tokens=*" %%A in (del_location.txt) do (
set array[!lines!]=%%A
set /a lines+=1
echo !lines!^) %%A
)
echo. & echo.
set /p choice=Which file do you want to delete? ^>
if %choice% lss 1 goto main
if %choice% gtr %lines% goto main
set /a choice-=1
del /Q !array[%choice%]!
echo. & echo.
echo "!array[%choice%]!" was deleted successfully ^^!
echo. & echo.
choice /c:yn /m "Do you want to delete more files? [y/n]" /n
if %errorlevel% == 1 goto main
if %errorlevel% == 2 exit /b
This script only provided for delete 1 file per turn. So if you want to batch delete, try to modify it yourself, ask anyone or me if you stuck at a point.
I think there's a way to delete files based on date other than forfiles method. Btw, dir SOURCE /o:d list and sort out files based on date.

'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