ECHO is off error when printing line to a file - batch-file

SET NEWLINE=^& echo.
FIND /C /I "telemetry.microsoft.com" %WINDIR%\system32\drivers\etc\hosts > NUL 2>&1
IF %ERRORLEVEL% NEQ 0 ECHO %NEWLINE%^0.0.0.0 telemetry.microsoft.com>>%WINDIR%\System32\drivers\etc\hosts
New to batch scripting. I seem to be getting and ECHO is off error which seems to have something to do with a variable not being set? Thanks

I would use the nonhacky method of outputting a newline via echo.:
IF ERRORLEVEL 1 (echo.&echo 0.0.0.0 telemetry.microsoft.com)>>%WINDIR%\System32\drivers\etc\hosts
Explanation of the problem:
After expansion the first echo (that is inside newline variable) doesn't have anything to output and thus it displays its status.
. should be at the beginning so that it's appended to the first echo to actually output the newline
No space needed between echo and %newline% so that the added dot follows echo
^ between %newline% and the following text is not needed as there's nothing to escape
Both echo commands should be surrounded with ( ) to indicate output redirection scope
So your original code might be like this:
SET NEWLINE=.^& echo
FIND /C /I "telemetry.microsoft.com" %WINDIR%\system32\drivers\etc\hosts > NUL 2>&1
IF %ERRORLEVEL% NEQ 0 (ECHO%NEWLINE% 0.0.0.0 telemetry.microsoft.com)>>%WINDIR%\System32\drivers\etc\hosts

Related

Manage accented characters in string

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

Check if substring exist in string from cmd/bat script

I want to check if count exists in mycountry or not, and then do some operations according.
My code snippet :
rem #ECHO OFF
cls
SET FILE="mycountry"
If true I want to run 3 statements and if false I want to run 3 other statements.
I have tried this combination:
Echo.%FILE% | findstr /C:"count">nul && (Echo.TRUE) || (Echo.FALSE)
But how to write multiple statements if the condition gets true? I don't wanna use any flag variable.
Below snippet is not working.
Echo.%FILE% | findstr /C:"count">nul &&
(
Echo.TRUE
echo "ran correct."
)
|| (Echo.FALSE)
You can use the %errorlevel% value combined with an if/else.
See the example below:
REM #echo off
cls
SET FILE="mycountry"
SET STR="TEST"
findstr %STR% %FILE% >nul
if %errorlevel% equ 1 (
goto searchError
) else (
goto searchSucces
)
:searchSucces
echo String %STR% found in file %FILE%
pause
exit
:searchError
echo String %STR% not found in file %FILE%
pause
exit
Your code, (integrated into a batch file), appears to work as expected:
#Echo Off
Set "FILE="
Set /P "FILE=Enter String: "
If Not Defined FILE Exit /B
Echo.%FILE% | findstr /C:"count">nul && (Echo.TRUE) || (Echo.FALSE)
Pause
In addition, the following two methods both appear to work as expected:
Using Echo and FindStr (as in your code):
#Echo Off
Set "FILE="
Set /P "FILE=Enter String: "
Echo=%FILE%|FindStr /IC:"count">Nul 2>&1&&(Echo TRUE
Echo Ran correct.
Timeout 3 /NoBreak>Nul
Echo Still running!)||Echo FALSE
Pause
Using variable substitution:
#Echo Off
Set "FILE="
Set /P "FILE=Enter String: "
If /I "%FILE:count=%"=="%FILE%" (Echo FALSE) Else (Echo TRUE
Echo Ran correct.
Timeout 3 /NoBreak>Nul
Echo Still running!)
Pause
If the examples above do not work for you, you should edit your question to include the actual code and strings you're using in your real world scenario. We cannot fix something we cannot see, especially if you don't fully explain the issue, (snippet is not working is a statement only, not an explanation).

Why does the error message appear when `IF` comparison is in the piped block command?

I simplified the code.
The following three are work.
for /L %a in (1,1,9) do #(if %a NEQ 0 (echo %a))
&
for /L %a in (1,1,9) do #(if not %a == 0 (echo %a))
&
(for /L %a in (1,1,9) do #(if not %a == 0 (echo %a)))|sort /R
But the next one didn't work,
(for /L %a in (1,1,9) do #(if %a NEQ 0 (echo %a)))|sort /R
What's the problem of NEQ in the piped block command?
more simplified,
This works, (if 3 == 3 echo yes)|sort
This doesn't work, (if 3 NEQ 2 echo yes)|sort
Part of my code.
#echo off
setlocal enabledelayedexpansion
set Unx_path=.....\bin\UnxUtils\
(
for /F %%a in ('^""%Unx_path%pclip.exe"^|"%Unx_path%sed.exe" -r "s/^^$/none/"^|"%Unx_path%sed.exe" -rf "script_file"^"') do #(
if not "%%a" == "none" (
"%Unx_path%grep.exe" -iEe "%%a" "file4search"|"%Unx_path%sed.exe" -r "s/^^[^,]+$/,&/";"s/^^([^.]+)[.][^.]+$/\1/"|"%Unx_path%gawk.exe" "BEGIN{FS=\",\";ORS=\" \"}{print $2}"|"%Unx_path%sed.exe" "s/%%a//I g";"s/.*/%%a &|/";"s/ -/ /g";"s/ |/\n/g";"s/ /\t/g";"s/~/\t/g"
) else (
echo none
)
)
)|"%Unx_path%gclip.exe"
exit /b
try this:
set "myline=if 3 NEQ 2 echo yes"
( %myLin^e%)|sort
or from batch file (double expansion works differently from batch file and the console):
set "myline=if 3 NEQ 2 echo yes"
( %%myLine%%)|sort
The "mystery" was solved by jeb here and here . Though you are facing the issue before the pipe it is the same bug because the cmd creates two threads on each side of the pipe.
Here's how the for loop can be made to work:
set "line=if %a NEQ 0"
(for /L %a in (1,1,9) do #( %lin^e% echo %a))|sort /R
For testing purposes the cmdcmdline variable can be used.
But this fails, when somewhere in the expression is a syntax error, as the complete code block will be dropped.
#echo off
echo dummy | (
echo %%cmdcmdline%
if 1 NEQ 2 echo Work
)
This results into "2" can't be syntactically evaluated here (From german "2" kann syntaktisch an dieser Stelle nicht verarbeitet werden.)
So exactly in the case of a syntax error, you can't see the cause!
I build a small debug variable for this case
#echo off
setlocal
(set \n=^
%=empty=%
)
set "debug=echo ### %%cmdcmdline%% ^) %%\n%%"
echo dummy | (
%debug%
if 1 NEQ 2 echo Work
)
When you add the %debug% you will see the cmdcmdline, but the code will not be executed, so you can examine how different modification take effect.
Like
...
| (
%debug%
break & if 1 NEQ 2 echo THIS FAILS
)
but
...
| (
%debug%
break ^& if 1 NEQ 2 echo This Works !!!
)
The trick is to add ^) %%\n%%, this closes the debug expression code block with ) and cancels further parsing of the remaing code by the linefeed %%\n%%.
Base on the idea & method from npocmaka & jeb.
Another way was found to solve the error of IF command in piped code.
TO ADD AN ESCAPE SPACE AT RIGHT POSITION
For EQU NEQ LSS LEQ GTR or GEQ,
ONE ^ has to be added after 1st compared string/number,
At least TWO SPACE have to be added between this ^ and EQU NEQ LSS LEQ GTR or GEQ.
Example,
echo pipe|IF 1234^ GTR 124 echo true
true can be replaced by %^cmdcmdline% to see the parser.
(IF 1234^ NEQ 124 echo right)|more
For IF DEFINED & IF EXIST
At least ONE SPACE has to be added after IF DEFINED or IF EXIST,
At least TWO SPACE have to be added before variable or file/folder,
ONE ^ has to be added between above added SPACE.
Example,
echo pipe|IF DEFINED ^ username echo true
echo pipe|IF EXIST ^ c:\windows echo true
true can be replaced by %^cmdcmdline% to see the parser
(IF DEFINED ^ username echo right)|more
(IF EXIST ^ c:\windows echo right)|more
But so far I didn't know why all of them have to work like above.
Something interesting,
echo pipe|if 123^ equ ====== 123 ====== echo %^cmdcmdline%
All = are ignored.
Updated (2016-04-23),
As npcmaka mentioned, =,, are also delimiters.
CASE 1
echo pipe|if not defined^===neq === echo %^cmdcmdline%
Outputs,
C:\Windows\system32\cmd.exe /S /D /c" if not defined=== neq echo %cmdcmdline%"
CASE 2
echo pipe|if not defined^ === neq === echo %^cmdcmdline%
Outputs,
C:\Windows\system32\cmd.exe /S /D /c" if not defined == = neq === echo %cmdcmdline%"
One space appears between ===.
CASE 3
echo pipe|if not defined^,,,neq ,,, echo %^cmdcmdline%
Outputs,
C:\Windows\system32\cmd.exe /S /D /c" if not defined,NEQ echo %cmdcmdline%"
Some phenomenon can be found, which may be known how to parse...
neq is identified as variable in all CASE. (do set neq=blah before will occur no output for all CASE)
In CASE 2, == in Delimiter === seems be identified as comparison and auto be added one space after
According to the rule in CASE 2, in CASE 3, input neq is lowercase and output NEQ is uppercase. But in CASE 1 & 2, due to existed == before neq, this time, neq is kept in lowercase, which is not identified as comparison.
So it has several steps in parser period. But has some bugs of adding or deleting delimiters. Right?
CASE 4
The next code seems trigger cmd executing infinitely,
echo pipe|if not defined^ == echo %^cmdcmdline%
it runs like if not "defined" == "echo" recursive command
I think the easiest work-around to get if statements working within pipes is the following (the outer surrounding pair of parentheses is mandatory):
(if^ comparison_expression conditional_command_line)
(if^ comparison_expression (conditional_command_line) else other_conditional_command_line)
I tested it with all keywords like not, exist and defined, else clauses, all possible comparison operators (==, EQU, NEQ, etc.), for either side of the pipe, and in cmd and batch-files.
Also I also successfully tested the if statements embedded within the body of a for loop (in which case the outer parentheses are no longer required).

batch ERRORLEVEL always 0

I'm facing an issue when trying to implement the ERRORLEVEL on my batch script. Basically what I'm trying to do is: look for the .txt files in the directory and if found; .txt will get listed, if not found; message error will occur.
The thing is that this directory will not always contain a .txt, sometimes it will be empty and when that happens my code will not work. Only when there is a .txt in the directory I'm getting my conditions to work (ERRORLEVEL = 0), but if empty; none of my conditions will. Not even the ERRORLEVEL will be printed in the cmd screen (I should see it as ERRORLEVEL = 1).
This is my code:
for /r "C:\Texts" %%a in (*.txt) do (
echo %errorlevel%
IF ERRORLEVEL 0 (
echo %%a
echo "I found your Text!"
) ELSE (
echo "I couldn`t find your Text!" )
)
What exactly is wrong with my ERRORLEVEL implementation?
Errorlevel 0 is always true.
Use
if not errorlevel 1
But your code doesn't set the errorlevel.
In your code there is not any command that set the errorlevel. This value is set by all external .exe commands (like find or findstr), and by certain specific internal commands (like verify that set errorlevel=1 when its parameter is wrong, or ver that always set the errorlevel=0.
You may explicitly set this value in your code this way:
rem Set the errorlevel to 1
verify bad 2> NUL
for /r "C:\Texts" %%a in (*.txt) do (
echo %%a
rem Set the errorlevel to 0
ver > NUL
)
if not ERRORLEVEL 1 (
echo "I found your Text!"
) else (
echo "I couldn't find your Text!"
)
However, you may also get a similar result using a Batch variable instead of the errorlevel...

Set errorlevel in Windows batch file

I am writing a batch script that will loop through each line of a text file, (each line containing a filename) check if the file exists and then runs the file and moves it.
Here is my batch script:
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (./ready/input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, set error flag if it doesnt
if not exist .\ready\%%i set errorlevel=2
echo return code is %errorlevel%
ECHO Run %%i if it exists
if errorlevel 0 call .\ready\%%i
ECHO Move %%i to archive if no error occured
if errorlevel 0 copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
ECHO Copy line of text to the new output.txt file if an error occured
if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k
)
Here is the output:
I do not understand why the "if errorlevel" is not working as expected... if the file does not exist (as in this example where it does not exist) it should NOT try to run the file, it should NOT copy the file, and it should echo a 2 not a 0
Edit 1: I was reading another SO Post regarding "delayed environment variable expansion" I am not sure if this issue is related
ERRORLEVEL and %ERRORLEVEL% are two different variables. That means your code with echo return code is %errorlevel% and if %errorlevel% NEQ 0 >>output.txt %%i, %%j, %%k is probably wrong.
ERRORLEVEL is builtin and used to fetch the result of the last command. You can use it like:
IF ERRORLEVEL 1 ECHO error level is 1 or more
ERRORLEVEL cannot be set, just like bash does not let you set ?= ...
%ERRORLEVEL% is an environmental variable. If %ERRORLEVEL% is set, then its used in your script when you use %ERRORLEVEL%. If %ERRORLEVEL% is not set AND if command extensions are enabled, then it falls back to ERRORLEVEL. ERRORLEVEL does not update %ERRORLEVEL%.
Raymond Chen has a good blog entry on it: ERRORLEVEL is not %ERRORLEVEL%. Some of the content in this answer was shamelessly lifted from it.
#ECHO OFF
SETLOCAL
DEL output.txt 2>nul
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, set error flag if it doesnt
if exist .\ready\%%i (set "errorflag=") ELSE (set errorflag=2)
CALL echo return code is %%errorflag%%
ECHO Run %%i if it exists
if NOT DEFINED errorflag (
call .\ready\%%i
ECHO Move %%i to archive if no error occured
if errorlevel 1 (SET errorflag=3) ELSE (ECHO copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i)
)
ECHO Copy line of text to the new output.txt file if an error occured
if DEFINED errorflag >>output.txt ECHO %%i, %%j, %%k
)
GOTO :EOF
Here's a rewritten procedure.
Note: output.txt is deleted at the start, else the >> would append to any existing file. 2>nul suppresses error messages if the delete fails (eg. file not exist)
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.
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 chnaged value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.
IF DEFINED var is true if var is CURRENTLY defined.
ERRORLEVEL is a special varable name. It is set by the system, but if set by the user, the user-assigned value overrides the system value.
IF ERRORLEVEL n is TRUE if errorlevel is n OR GREATER THAN n. IF ERRORLEVEL 0 is therefore always true.
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray spaces at the end of a line are NOT included in the value assigned.
The required commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO COPY to COPY to actually copy the files.
I used the following input.txt:
seterr1.bat, J1, K1
seterr5.bat,J2,K2
seterr0.bat,J3 K3
seterr5.bat, J4, K4
notexist.bat, J5, K5
With existing files seterr*.bat which contain
#ECHO OFF
EXIT /b 1
(where the 1 in the last line determines the errorlevel returned)
and received the resultant output:
Check seterr1.bat exists, set error flag if it doesnt
return code is
Run seterr1.bat if it exists
Move seterr1.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check seterr5.bat exists, set error flag if it doesnt
return code is
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check seterr0.bat exists, set error flag if it doesnt
return code is
Run seterr0.bat if it exists
Move seterr0.bat to archive if no error occured
copy .\ready\seterr0.bat .\archive\__J3_K3_seterr0.bat
Copy line of text to the new output.txt file if an error occured
Check seterr5.bat exists, set error flag if it doesnt
return code is
Run seterr5.bat if it exists
Move seterr5.bat to archive if no error occured
Copy line of text to the new output.txt file if an error occured
Check notexist.bat exists, set error flag if it doesnt
return code is 2
Run notexist.bat if it exists
Copy line of text to the new output.txt file if an error occured
Note that the COPY is merely ECHOed as I mentioned earlier.
and output.txt
seterr1.bat, J1, K1
seterr5.bat, J2, K2
seterr5.bat, J4, K4
notexist.bat, J5, K5
Use something like the following subroutine:
:return
ECHO #exit /b %1 >ret.cmd
CALL ret.cmd
GOTO :eof
Then use it like this:
:Attempt
SETLOCAL
CALL somethingThatFails
SET retcode=!errorlevel!
CALL somethingThatPasses : don't care about the errorlevel here
CALL :return !retcode!
ENDLOCAL
CALL :eof
So, the whole thing would looke something like:
test.cmd...
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :Attempt
IF !errorlevel! NEQ 0 (ECHO Attempt Failed) ELSE (ECHO Attempt succeeded!)
GOTO :eof
:Attempt
SETLOCAL
CALL somethingThatFails
SET retcode=!errorlevel!
CALL somethingThatPasses : don't care about the errorlevel here
CALL :return %retcode%
ENDLOCAL
CALL :eof
:return
ECHO #exit /b %1 >return.cmd
CALL ret.bat
GOTO :eof
somethingthatfails.cmd...
DIR some command that fails >nul 2>&1
somethingthatpasses.cmd...
DIR >nul 2>&1
The one side effect of this is a file laying around called ret.cmd. I usually use an :end subroutine that does cleanup and would delete it.
There is an easy way to set the %errorlevel% with a trick I learned several years ago:
:: force errorlevel to 1
#(call)
echo %errorlevel%
:: force errorlevel to 0
#(call )
echo %errorlevel%
pause
The space after call is necessary to set the %errorlevel% to 0.
Update: After researching this, I found a reference here.
For posterity, when specifically setting it to 0, I like
ver >nul
ver.exe always returns 0.
This is designed to execute the %%i item only if it exists and follow through with checking for errors and move or log. if the %%i item doesn't exist then it will do nothing.
REM Loop through each line of input.txt
FOR /F "tokens=1-3 delims=, " %%i IN (.\ready\input.txt) DO (
ECHO.
ECHO.
ECHO.
ECHO Check %%i exists, execute it if it does
if exist .\ready\%%i (
call .\ready\%%i
ECHO Move %%i to archive if no error occured
if not errorlevel 1 (
copy .\ready\%%i .\archive\%mydate%_%mytime%_%%j_%%k_%%i
) else (
ECHO Copy line of text to the new output.txt file if an error occurred
>>output.txt %%i, %%j, %%k
)
)
)
for me, simple use of cmd /c exit 2 worked to set the errorlevel and use it locally in a batch file and even after it ended to ask for the errorlevel outside:
set errorlevel=2
:
cmd /c exit %errorlevel%
:
if errorlevel 3 echo 3
if errorlevel 2 echo 2
if errorlevel 1 echo 1
if errorlevel 1 echo 0
Results
>test.bat
2
1
0
>if errorlevel 2 echo 2
2

Resources