Trying to put a batch file together that will find a string within a text file. Only obstacle is I want to find values that occur in the evening(PM) and NOT in the Morning(AM).
I am trying to find "XBR Upload successful" however only if exists in the PM:
Exert from the log file:
[3:35:07AM] XBR Upload successful.
I have the following:
#echo off
findstr /M "[*PM] XBR Upload successful." C:\Test.log
if errorlevel = 1 GOTO NOMATCH
if errorlevel = 2 GOTO MATCH
Wildcards * does not seem to be working for me. Seems to find [*PM] even though in the log file no PM timing exists yet.
Any guidance appreciated!
You should try like that :
#echo off
Set InputFile=c:\Test.log
Set OutPutFile=c:\LogFile.txt
Type "%InPutFile%" |findstr /M /R /C:"\[.*PM\] XBR Upload successful." > %OutPutFile%
If "%Errorlevel%" EQU "1" GOTO :NOMATCH
If "%Errorlevel%" EQU "0" GOTO :MATCH
:MATCH
Color 0A
echo MATCH
pause
Start "" %OutPutFile%
Exit /b
:NOMATCH
Color 0C
echo NO MATCH
pause
Exit /b
Your code does not work as you want because you are misinterpreting what * does in regular expressions. Normally it is a quantifier, not a wild card.
The expression x* means "match 0 or more x characters". But your case is a bit special because the * appears within square brackets, so it is interpreted as a character class. [*] matches a * literal character.
The expression you wanted was \[.*]
. is the wildcard, and \ escapes the [ so that it is a literal instead of the beginning of a character class.
Also, you must use the /C option if your search string includes spaces. Since you are doing a regular expression, then you also need the /R option to override the /C string literal interpretation behavior.
findstr /R /C:"\[.*PM] XBR Upload successful." C:\Test.log >nul && goto Match || goto NoMatch
But as others have indicated, you could probably get away with a simpler string literal search, without any wildcard.
findstr /C:"PM] XBR Upload successful." C:\Test.log >nul && goto Match || goto NoMatch
Related
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
I am trying to make a simple batch script to compare two files and open them if it found a difference.
So I use FC to compare the files; if no difference is found, it'll send me this text:
Comparaison des fichiers FILE1 et FILE2
FC : aucune différence trouvée
As you can see, it's in French, and it contains accented characters. That's my problem.
So here's my code so far:
#echo off
set "FILE1=file1path"
set "FILE2=file2path"
set "edit=editorpath"
for /F "tokens=*" %%F in ('fc /A /C /N "%FILE1%" "%FILE2%"') do (
set "DIFF=%%F"
)
set "NODIFF=FC : aucune différence trouvée"
if /I "%DIFF%"=="%NODIFF%" (
echo "no difference found"
) else (
start "" %edit% %FILE1%
start "" %edit% %FILE2%
)
The problem is in "%DIFF%==%NODIFF%: the two vars are always different, even when they should be the same (I've tried with or without "" for the set and the If comparison). To see this, I've tried to echo both var. Here's what I get:
echo %DIFF%
FC : aucune différence trouvée
echo %NODIFF%
FC : aucune diff├®rence trouv├®e
seems that the encoding is not the same. Most answer I've found for it was to use some chcp (with different values). If I understand it well, it's supposed to change the encoding so that accented characters "works". So i've tried to add
chcp 65001>nul
to my code, but then I got this result:
echo %DIFF%
FC[?]: aucune diff[?]rence trouv[?]e
echo %NODIFF%
FC : aucune différence trouvée
So now the second string is ok, but the first one no more. I've tried others numbers instead of 65001, but they would just make both strings wrong (and not in the same way).
ofc I can also do something like this:
chcp 850
echo %DIFF%
FC : aucune différence trouvée
chcp 65001
echo %NODIFF%
FC : aucune différence trouvée
But then in the IF statement, I can't have change the chcp while doing the comparison.
Have someone an idea on how to make it works?
PS: i'm using cmd and NOT POWERSHELL on windows10 64bits with MINGW.
If you really wanted to do what you intended, then just narrow your result to verifying just the known standard characters:
#Echo Off
Set "FILE1=file1path"
Set "FILE2=file2path"
Set "edit=editorpath"
For /F Delims^=^ EOL^= %%G In (
'""%__APPDIR__%fc.exe" /A /C /N "%FILE1%" "%FILE2%""') Do Set "DIFF=%%G"
If /I Not "%DIFF%"=="%DIFF: aucune diff=%" (Echo no difference found) Else (
Start "" "%edit%" "%FILE1%"
Start "" "%edit%" "%FILE2%")
Exit /B
Essentially you're case insensitively comparing two strings, the second with the substring aucune diff removed. If they're not the same, then the substring was part of the string. Please note that as English versions would use FC: no differences encountered, your current idea is still language/locale dependent.
However, in my opinion, there's no need for a for-loop or to jump through hoops with language/locale dependent strings. You can simply use the returned exit code, (ErrorLevel):
2 Could not open at least one of the files.
1 Differences found.
0 No differences encountered.
-1 Invalid syntax used.
#Echo Off
Set "FILE1=fileone.cmd"
Set "FILE2=filetwo.cmd"
Set "FILE3=filethree.cmd"
"%__AppDir__%fc.exe" /A /C /N "%FILE1%" "%FILE2%" >NUL 2>&1
If %ERRORLEVEL% Equ 2 (Echo Could not open at least one of the files.
) Else If %ERRORLEVEL% Equ 1 (Start "" "%edit%" "%FILE1%"
Start "" "%edit%" "%FILE2%") Else If %ERRORLEVEL% Equ 0 (
Echo No differences encountered.) Else Echo Invalid syntax used.
Pause
GoTo :EOF
Although for your purposes, you could simply use && and || to conditionally determine if the previous command was successful or not:
#Echo Off
Set "FILE1=file1path"
Set "FILE2=file2path"
Set "edit=editorpath"
%__APPDIR__%fc.exe /A /C /N "%FILE1%" "%FILE2%" >NUL 2>&1 && (
Echo no difference found) || (Start "" "%edit%" "%FILE1%"
Start "" "%edit%" "%FILE2%")
Exit /B
A working script that moves various files based on file name runs successfully. After the script is done it will check two directories for any lingering files using IF EXIST *.txt. This works great except I've noticed some files with no extension. These were not an issue before and since that cannot be helped due to processes out of my control, I need to amend my script.
My only idea is the following code. Bear with as there are two conditions:
:check1
PUSHD "\\UNC\path1" &&(
DIR /A-D *.
IF %errorlevel% NEQ 0 GOTO check2
) & POPD
:add1
ECHO Add note to the log file
:check2
PUSHD "\\UNC\path2" &&(
DIR /A-D *.
IF %errorlevel% NEQ 0 GOTO laststep
) & POPD
:add2
ECHO Add note to the log file
:laststep
Some other code before exiting
This should run DIR on the path and if files without extensions exist, it will have %errorlevel% zero and move on to the next check. If there are no files present, it will have %errorlevel% not zero (likely 1) and it will append some text to the log before the next check. Check two will do the same.
This seems to be awfully complicated and I am not able to find a "one-liner" solution that is as easy as IF EXIST. I realize I can use *. but that returns directories as well and may result in an incorrect %errorlevel%.
Updated Code
Where I normally set my variables, I also SET the two paths to run DIR against. This way they can be used more easily elsewhere and I bypass the UNC Path error I normally get - reasons for that are unknown to me. The updated file check, used only for files without an extension, is:
DIR %p1% /b /a-d|FIND /v "." && ECHO Found 1 >> %log%
DIR %p2% /b /a-d|FIND /v "." && ECHO Found 2 >> %log%
FINDSTR /I "Found" %log%
IF %errorlevel% EQU 0 GOTO stillthere
:nofiles
Some code
GOTO domore
:stillthere
Some code
:domore
Other code before exit
Thank you for the responses, I've learned from this.
Is this what you want to find?
dir /b /a-d |find /v "."
#ECHO OFF
SETLOCAL
:check1
PUSHD "u:\path1"
DIR /A-D *. >NUL 2>NUL
IF %errorlevel% EQU 0 ECHO Add note \path1 to the log file
POPD
PUSHD "u:\path2"
DIR /A-D *. >NUL 2>NUL
IF %errorlevel% EQU 0 ECHO Add note \path2 to the log file
POPD
:laststep
:: Some other code before exiting
GOTO :EOF
Your problems include:
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.
and you are potentially jumping out of a PUSHD/POPD bracket which would mean your POPD won't necessarily restore your starting directory.
(Note that I used u:\ rather than a server to suit my system)
You are already using && to verify PUSHD worked. You can do the same with your DIR /-D. I find it makes life easier. Also, you probably want to hide any error message if *. is not found, especially since that is the expected condition. I also hid the display of any files that might be found, but you can certainly get rid of that redirection. You might also want to hide error message if PUSHD fails, but I didn't implement that.
PUSHD "\\UNC\path1" && (
DIR /A-D *. 1>nul 2>nul && ECHO Add note to the log file
POPD
)
PUSHD "\\UNC\path2" && (
DIR /A-D *. 1>nul 2>nul && ECHO Add note to the log file
POPD
)
ECHO Some other code before exiting
I have the following line in a batch script
for %%a in (*.rmt) do (findstr /C:" model='" %%a)>tmp.par
When I run this on an empty folder, the errorlevel is still 0.
However, if I replace *.rmt with a filename, say x.rmt, which doesnt exist in the folder either, the errorlevel becomes 1.
Ideally, if there are no RMT files in the folder, shouldnt the errorlevel!=0?
I require this For loop to work on *.rmt, as there might be 0 to multiple RMT files in a folder. Please help.
Thanks.
Note: If the string " model='" exists in one RMT file, it will compulsorily be present in all the other RMT files(if any) in the folder.
The findstr is never executed if there are no matches to the *.rmt, hence the errorlevel remains unchanged.
When you use x.rmt, FOR changes behaviour - it's no longer looking for a filename matching, it's looking at a particular string - which may or may not be a filename, which may or may not exist.
You could deliberately set errorlevel before the for
#ECHO OFF
SETLOCAL
ECHO y|FIND "x">nul
for %%a in (*.rmt) do (findstr /C:" model='" %%a)
ECHO errorlevel=%errorlevel%
GOTO :EOF
which will return errorlevel 1 unless the match is found.
Try this:
#echo off
for /F "delims=" %%i in ('dir /b "path_to_dir\*.rmt"') do (
:: echo Folder is NON empty
findstr /C:"model='" %%i >> C:\testlog.log
goto :EOF
)
No, the FOR command never sets the ERRORLEVEL <> 0 if there are no iterations.
Yes, the following command reports ERRORLEVEL=1:
for %%a in (notExists.rmt) do (findstr /C:" model='" %%a)>tmp.par
But that is because the simple FOR simply lists the string(s) within the IN() clause if they do not contain wildcards. It doesn't bother checking to see if the file exists. So your FINDSTR command is actually raising the error because it cannot find the file, not the FOR statement.
Your command is flawed in that each iteration overwrites the previous tmp.par. That can be easily fixed by adding an extra level of parentheses. This also will create an empty tmp.par if no files were found or if none of the files contained the search string. The ERRORLEVEL cannot be relied upon because its value will not have been set if no files were found, or it may be 0 or 1 depending on if the last file contained the search string.
(for %%a in (*.rmt) do (findstr /C:" model='" %%a))>tmp.par
If you don't mind having a filename: prefix on each line of output, then you can simplify your code to:
findstr /C:" model='" *.rmt >tmp.par 2>nul
This also will create an empty tmp.par file if no files were found, or if none of the files contain the search string. But now the ERRORLEVEL will be reliable. The ERRORLEVEL is 1 if no files are found or if no files contain the search string. Otherwise the ERRORLEVEL will be 0.
I wonder is there any way to check that if a label exist in a batch file?
If %input%=ABC (
If Label ABC Exists (
Goto ABC
)
)
How can I do this?
Any help will be appreciated.
findstr /i /r /c:"^[ ]*:%input%\>" "%~f0" >nul 2>nul && goto %input%
Search the label in the current batch file and if no errorlevel, label exists
EDITED - I realized there was an error on the way i was handling the end of the label and was going to edit the answer (it has been edited anyway) and i see the dbenham aclarations. He saw the error and corrected it. Thank you. Nice answer as always, BUT this is worse than what you have exposed.
In this moment i have only a XP to test, but this is what works for me. If anyone can test on later windows versions, please.
First problem: the start of the label. As usual dbenham is correct, and any character in the set [;=,<space><tab>0xFF] can precede, single or repeated, the colon of the label. But, as far as it is the first character on the line, and it does not repeat, almost any character can precede the colon of the label (one exception is other colon). So, the following will work without problems
call :test
goto :test
echo this will not be echoed
X=;=:test
echo Hello
NO, this it not a valid line, if the parser try to execute the label line, a "command not recognized" error will happen, BUT is a valid label to call or goto.
Second problem: end of the label. As dbenham identified, most of us place a space and the list of arguments when the label is used to define a function/procedure. This was the error i realized and what has been corrected in my original answer. BUT, a space (and obviously the end of line) is not the only allowed characters after the label name. So, In the previous sample, any of the following labels will work
:test arguments
:test:arguments
:test>arguments
:test<arguments
:test&arguments
And yes,in this case they are valid commands to the parser and are valid labels
AND, of course, the two "problems" can happen at the same time
call :test
goto :test
echo this will not be echoed
< ;;:test:;; > This WORKS
echo Hello
POST EDIT 1 - It seems all this work was done years ago at dostips.com. Thanks to all who compiled the exaustive list referenced in comments. Next time, i'll search first.
POST EDIT 2 - I've been trying to deal with the limitations of findstr to include all cases. Well, there is no way. There are too many limitations, starting with the impossibility of include the 0xff character in a regular expression.
For a robust and simple solution, the answer from dbenham is the best option.
For a more robust, but STILL INCOMPLETE, no bulletproof version, and more complex than dbenham's answer
#echo off
for /l %%i in (1 1 10) do (
call :testLabelExist "test%%i" && echo Label [test%%i] exist || echo Label [test%%i] does not exist
)
exit /b
:test1
:test2
:test3
x:test4
::test5
:test6:
:test7#
:test8 parameters
:test9 parameters
:test10:myData
:testLabelExist
for /f "delims=" %%t in (
'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /d /c #echo 0x09"'
) do (
findstr /i /m /r /c:"^[^:]*[ %%t]*:%~1[ %%t:;,=+]" /c:"^[^:]*[ %%t]*:%~1$" "%~f0" >nul 2>nul
)
exit /b %errorlevel%
And it still leaves out quoted label names, just to name one failure point.
Here is a refined, more robust version of the MC ND answer. (The original answer, his edit addresses many of these same points).
Labels are case insensitive, so the search should be case insensitive.
A valid label may have additional text after the label, so there are two searches required. The additional text is frequently used as documentation. For example: :label documentation is still a valid label.
findstr /ri /c:"^ *:%input% " /c:"^ *:%input%$" "%~f0" >nul 2>nul && goto %input%
The above should work in most situations, but there are a few unlikely conditions that could cause it to fail.
Any of the following characters can appear before the label - , ; = <space> <tab> <0x255>. They all are treated as spaces when they precede a label. But the search above only allows for <space>. A [class] expression could be used, but including <tab> and <0x255> can be awkward.
In a similar fashion, the label can be terminated by some characters other than <space> (a different list).
The label could contain regular expression meta-characters.
The FINDSTR $ anchor only recognizes <CR><LF> as end of line, so the search can fail if the script uses Unix style <LF> line endings.
The search could be refined to handle most of the above conditions. But it is simpler to simply avoid those conditions in your code. I don't think it is possible to define a bullet proof search using a single FINDSTR. A bullet proof search would require at least two FINDSTRs, and one would have to use the /G:file option - yuck.
Try the following code:
echo off
set "label=sub"
REM next line to reset errorlevel to zero:
(call )
call :%label% 2>nul || (echo %label% not found & exit /b 1)
echo back from %label%
Exit /b 0
:sab
echo here we are
first I would just rethink everything and every time I created a label, I'd go to the top in a "constants or variables area" and predefine each of the labels I had in the file... so after writing, I'd go and manually check all the labels and just do a
for %%l in ( :label1 :label2 :label3 :label4 :EOF ) do set x_labelexistconst_%%l=.
where :label1 :label2 :label3 are all eyeballed and manually entered -- think of it all as if CMD required items to be declared before use like many languages do, but if really lazy you can automate this with findstr as they have with the subroutine examples and have a line like this instead
for /F "tokens=1" %%l in ( 'findstr /i /r /c:"^[ ]*:[a-z0-9]*" "%~f0"' ) do set x_labelexistconst_%%l=.
in the code either of those loaded array of variables would it would be used like
if defined x_labelexistconst_%input% goto %input%
if defined x_labelexistconst_%input% call %input%
… the advantages of this methodology would be that there wouldn't be a CALL and then new findstr "EACH AND EVERYTIME" you did a "if exist label", but instead you would just do it "ONCE" at the beginning to preload the list/array/construct of labels.
I think this option also has the advantage of being able to do && || after the goto call because error levels aren't used to check for labels so
if defined x_labelexistconst_%input% (call %input% && echo call ok || echo call bad)
Ultimately searching file with findstr and CALL and GOTO (which reread the original bat file or something) are both very slow commands and avoiding their use or limiting their use to once or never is the best option for bat files -- especially over slow network connections where the bat file may be large. And it makes the entire script smaller simpler and easier to understand.
I didn't debug this or test this lots but it just seems like a better idea.
If you add this to the top of your function:
if "%~1" == "test" goto:eof
You can use this to check if your function exists:
call :myFunction test 2>nul
if %errorlevel% == 0 call :myFunction
Example:
call :myFunction test 2>nul
if "%errorlevel%" == "0" (call :myFunction) else (echo function does not exist)
goto:eof
:myFunction
if "%~1" == "test" goto:eof
echo Function exists
goto:eof