2

I have this line:

exec 3<>/dev/tcp/127.0.0.1/9091 > /dev/null 2>&1 ||  { PORT_IS_FREE="yes"; };

which is checking if port 9091 is available. If a connection cannot be made, then I get this error:

my-script: connect: Connection refused
my-script: line 6: /dev/tcp/127.0.0.1/9091: Connection refused

of course, this error is not bad news, it means the port is free. How can I prevent the error trace from being logged? I tried sending stdout/stderr to /dev/null, but apparently that's not doing the trick.

Bonus:

I have set -e at the top the script - everything will halt if the connection is refused - how can I prevent that halt, if an error is expected on a particular line like the one above?

So I have two goals:

  1. Get rid of the error message, because it's expected and I don't need my library users to see it.

  2. Ignore the error as it's expected, and I still want to use set -e, if possible.

2 Answers 2

2

To keep using set -e but still allow a known error, use this incantation:

/bin/false || :

This uses the || operator to 'consume' the error so that it is deemed nonfatal to an environment in which set -e is active.

You're already using 2>/dev/null to suppress standard error, so are you sure the error is coming from the line you cite here? Also, rather than using exec, I propose as a more readable alternative:

if ! nc -z localhost 9091 1> /dev/null 2>&1; then
    port_free="yes"
fi

Since the return code of nc is checked by the if statement, this is also safe after set -e.

7
  • thanks, I am not sure how to use the incantation in the context of the script above...my guess is /bin/false || exec 3<>/dev/tcp/127.0.0.1/9091 > /dev/null 2>&1 is that right? I think using netcat like you have is prob better, yes. Commented Jul 12, 2017 at 20:56
  • I am pretty certain 1> is equaivalent to >, and I find the latter much more readable. Commented Jul 12, 2017 at 20:57
  • if I use set -e with the nc command you have in the if statement it will conk out, so I need to use /bin/false before the nc command. Commented Jul 12, 2017 at 21:00
  • I get /bin/false: No such file or directory....hmmm Commented Jul 12, 2017 at 21:14
  • 2
    @AlexanderMills he meant : code which ends up returning non zero || : (false is just an exemple of a code returning non-zero) Commented Jul 14, 2017 at 7:52
2

To properly redirect both the error and output streams to /dev/null, thereby suppressing the error message, you need to group the whole exec command:

{ exec 3<>/dev/tcp/127.0.0.1/9091; } > /dev/null 2>&1 || PORT_IS_FREE="yes"

exec is a shell builtin, so it is hard to predict for me how it will behave, however I assume that even though it is able to treat < and > specially, the whole operation is still carried out in several steps, the streams of only the last of which get redirected. Someone who knows more about the workings of exec might be able to shed some light on this.

EDIT: I do not know why you needed brace-grouping for your variable assignment since it's a single command, but you can still use braces in conjunction with my examples if you want to, instead of the bare assignment I used.

As for ignoring the error, you could terminate the command after the redirection with ;instead of using its exit status via || :

{ exec 3<>/dev/tcp/127.0.0.1/9091; } > /dev/null 2>&1 ; PORT_IS_FREE="yes"

Of course, in this case, there is the issue that you will not be able to catch any other error either. I just tried and the exit status is 1 after the error in question happens. If other codes are returned on different errors (which I doubt actually, since 1 is a very generic error code), you could test for 1. EDIT: This solution also comes with the benefit of not exiting when code 1 is returned after the exec, when set -e is used:

{ exec 3<>/dev/tcp/127.0.0.1/9091; } > /dev/null 2>&1 || [ "$?" = 1 ] && PORT_IS_FREE="yes"

Otherwise, you might use grep to try and match the error message, which is more reliable in my opinion:

socketOpenOutput="$({ exec 3<>/dev/tcp/127.0.0.1/9091; } 2>&1`)"
socketOpenErrorCode="$?"
if [ "$socketOpenErrorCode" != 0 ]; then
    if ! echo "$socketOpenOutput" | grep 'connect\s*:\s*Connection refused' >/dev/null; then
        echo "An unexpected error happened when opening the socket!"
        exit 1
    fi
fi

PORT_IS_FREE="yes"

This one, however, does not work well with set -e .

1
  • Here is an example of how I was able to integrate this solution into an if statement; Commented Jan 4, 2021 at 18:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .