Hashim Aziz
Hashim Aziz

Reputation: 6127

Conditional programming with batch - can't direct STDERR to NUL

I'm having a problem with what seems like it should be a fairly basic piece of a script.

ECHO !url1! >> !hostsfile! && ECHO Successfully added !url1! to file. || 2>&1>NUL && ECHO Unable to write !url1! to file. && GOTO :FAIL

It's a conditional that tries to write a URL to a given file, and if access to that file is denied, prints an error message. Unfortunately, CMD also generates its own error message, so at the moment, as output, I'm getting the default error "Access denied" followed by my own "Unable to write www.url.com to file."

I've tried without luck to pipe the default error message to NUL, but even with all my reading up on it, my knowledge on how piping works and the syntax for it is still sparse, and I'm not getting anywhere.

Also, as an aside: is there any way to split that line to be more manageable without causing errors?

Trying stuff like:

ECHO !url1! >> !hostsfile! && ECHO Successfully added !url1! to Hosts file. 
|| 2>&1>NUL && ECHO Unable to write !url1! to Hosts. && GOTO :FAIL

or

ECHO !url1! >> !hostsfile! && ECHO Successfully added !url1! to Hosts file. || 
2>&1>NUL && ECHO Unable to write !url1! to Hosts. && GOTO :FAIL

...results in "unexpected character" errors.

NB. Exclamation marks are being used for variables because of delayed expansion.

Upvotes: 4

Views: 852

Answers (3)

aschipfl
aschipfl

Reputation: 34949

There are several issues:

  1. 2>&1>NUL is invalid redirection syntax, you cannot concatenate multiple redirection operators like that. I assume you want to redirect the STDERR stream to the nul device, so it should read 2>nul or 2> nul.
  2. You can only redirect the input/output of a command, hence you cannot use redirection without any command. You are trying to (incorrectly) redirect 2>&1>NUL, but there is no command.
  3. The echo command does actually not produce the error message Access is denied., it is caused by a failed redirection. Therefore you must enclose the redirected echo command in parentheses in order to be able to suppress the error message by the 2> nul redirection.
  4. Note that the echo command itself does not set the ErrorLevel pseudo-variable nor the exit code1. However, a failed redirection (<, >, >>) sets the exit code, which is what you are interested in.
  5. You need to ensure that the goto command is only executed in the || branch, so you should put that entire portion in between parentheses.
  6. When you write echo Text > file.ext, the SPACE in between Text and > is also output. To avoid that, either simply remove it like echo Text>file.ext, place a pair of parentheses around the echo command like (echo Text) > file.ext, or revert the syntax like > file.ext echo Text (which is the way I prefer as it is the most general one).
  7. It is quite similar when you write echo Text & goto :LABEL, the SPACE in front of & is output as well. To avoid that, remove it, or enclose the echo part within parentheses.
  8. In your further attempts you do not tell cmd that the two lines belong together. Line continuation is done by ending a line with a caret ^, so the next one is concatenated2. The next line should be preceded by (at least) a SPACE. Alternatively, parenthesised blocks of code may span multiple lines, so you could also end the first line like ... || ( and have the next line like ... ) for them to be considered as a single command line.

Here is the fixed code:

(>> "!hostsfile!" echo !url1!) 2> nul && (echo Successfully added !url1! to file.) || (echo Unable to write !url1! to file.& goto :FAIL)

Or (using line continuation):

(>> "!hostsfile!" echo !url1!) 2> nul && (echo Successfully added !url1! to file.) ^
    || ((echo Unable to write !url1! to file.) & goto :FAIL)

Or even (using parenthesised blocks):

(>> "!hostsfile!" echo !url1!) 2> nul && (
    echo Successfully added !url1! to file.
) || (
    echo Unable to write !url1! to file.
    goto :FAIL
)

1) These two values are almost always are the same, but there are rare cases where they differ; the && and || operators do actually not care about ErrorLevel, they rather use the exit code.

2) The trailing caret ^ lets cmd ignore the next line-break and escape the following character, so two lines echo A^ and & echo B cause the output A& echo B as the & appears escaped; to achieve two lines of output A and B, change the second line to SPACE + & echo B, so the SPACE is escaped but the ampersand is not.

Upvotes: 6

rojo
rojo

Reputation: 24476

Firstly, if you want to redirect stderr to NUL, then just 2>NUL. To choke off the stderr of echo, you should encapsulate the echo statements within an additional generation of parentheses, and redirect 2>NUL for the entire code block.

And while you're adding parenthetical code blocks, to answer your "as an aside" question, you can pretty much put parentheses before or after any command. This is useful when writing compound command clauses / conditionals to stick with one command per line and make your code more readable.

Here's an illustration:

@echo off & setlocal

set "hosts=%systemroot%\system32\drivers\etc\hosts"
set "url=169.254.0.1        test"

2>NUL (
    setlocal enabledelayedexpansion
    >>"%hosts%" (echo !url!) && (
        echo Successfully added.
        endlocal
    ) || (
        echo Addition failed.  Try running this as administrator.
        exit /b 1
    )
)

exit /b

This successfully suppresses any "Access is denied." message, echoing only your specified success or fail message as appropriate.

By the way, I did (echo !url!) to avoid echoing the trailing space, and just because I think it's prettier / easier to read than echo !url!&&.

Upvotes: 1

user6811411
user6811411

Reputation:

I'd try something like this:

Set hostsfile=%Systemroot%\System32\drivers\etc\hosts
Findstr /I "!url1!" "!hostsfile!" 2>&1 >NUL &&(Echo !hostsfile! already contains !url1! & goto :whereever)
>> !hostsfile! ECHO !url1! 
Findstr /I "!url1!" "!hostsfile!" 2>&1 >NUL ||(Echo Couldn't add !url1! to !hostsfile! & goto :whereever)

Upvotes: 0

Related Questions