if [ exec 1>/dev/null 2>/dev/null 3>/dev/tcp/localhost/4000 ] ; then
This code runs `[ exec ]` (which tests if "exec" is a non-empty string, thus it returns true) and redirects its output and error to /dev/null, and fd 3 to /dev/tcp/whatever. If the socket opening fails, `[ exec ]` is not even executed, and the overall command returns nonzero. Completely equivalent to `: 3>/dev/tcp/localhost/4000`, which also returns nonzero if the socket can't be opened.
It happens to work for the intended purpose, but not for the reasons the author intended.
In exec.sh line 1:
if [ exec 1>/dev/null 2>/dev/null 3>/dev/tcp/localhost/4000 ] ; then
^-- SC1009: The mentioned syntax error was in this if expression.
^-- SC1073: Couldn't parse this test expression. Fix to allow more checks.
^-- SC1014: Use 'if cmd; then ..' to check exit code, or 'if [[ $(cmd) == .. ]]' to check output.
^-- SC1072: Expected test to end here (don't wrap commands in []/[[]]). Fix any mentioned problems and try again.
For more information:
https://www.shellcheck.net/wiki/SC1014 -- Use 'if cmd; then ..' to check ex...
https://www.shellcheck.net/wiki/SC1072 -- Expected test to end here (don't ...
https://www.shellcheck.net/wiki/SC1073 -- Couldn't parse this test expressi...
I'm not sure if the code example changed on the linked page after your comment, but I see this code:
#!/bin/bash
if exec 3>/dev/tcp/localhost/4000 ; then
echo "server up!"
else
echo "server down."
fi
Which also confused me for similar reasons to your comment. I'm not familiar with exec, but generally non-zero return means a fail, and zero means success. So why is the "server up" for non-zero result?
indeed you are right. Thanks for the reply.
I'm too accustomed to seeing code like:
if [ $? -eq 0 ]; then echo "pass";else echo "fail";fi
But without the expression '[ ... ]' shell/bash does indeed treat zero return code (success) as boolean true.
For whatever reason I have not seen much (if any) bash code that simply uses:
You really really should use if COMMAND logic, in the event that all you care about is zero or non-zero result. I find it easier to follow for shell control flow.
yep, I think that's the key point I need to drill into my head. The unix/linux idiom where 0 is true/success and non-zero is a failure can be counter-intuitive when combined with "if" (at least for me, hence my mistake reading the code). In most/other computing contexts "if 0" is never taken. I think this might explain why I've seen a lot more bash code that is explicitly written in the form more familiar to "if" usage in other languages (pseudo-code): "if res == 0". But "if COMMAND" is taking care of the interpretation of the return code, so doing the explicit compare is redundant.
Hi I'm the author! Yes, I wrote this code quite quickly and when I originally posted this on reddit someone pointed out my mistake. The more you know, I suppose.
Note that this is a bashism, netcat is (a bit, due to BSD vs GNU) more portable. I think Hurd can provide similar functionality at the filesystem level thanks to its translators. Plan 9 likely can, too, and it would be possible to cook something together with FUSE.
I leveraged this some time ago to sent WOL packets. I think it went something like:
echo -e [16 times the target MAC written as \xFF] > /dev/udp/255.255.255.255/9
BTW, I'm not entirely sure how broadcast IPv4 addresses work. I think they are set by the DHCP server, and the computer sends packets to these addresses as MAC broadcasts? (leaving the dest IP as the configured broadcast one).
> netcat is (a bit, due to BSD vs GNU) more portable
Note that GNU doesn't have a netcat implementation. There's a netcat implementation that calls itself "GNU netcat", but it isn't actually associated with GNU, and the authors are jerks for naming it that.
But also, there are more netcat implementations than just 2!
The broadcast address of a given subnet is "!netmask | hostIp" e.g. if the host IP is 192.168.1.12/24 then it's 192.168.1.12 | 0.0.0.255 = 192.168.1.255. The "human" way of thinking about it is it's the last address in the subnet. The IP4 broadcast address for the current network (even if the client doesn't known what it is) is 255.255.255.255.
Once the packet arrives at the destination subnet (which can be immediately or several routing hops away) it will (in the case of Ethernet) you're right that's to the FF:FF:FF:FF:FF:FF destination MAC.
This being a bash-ism is really misleading. This looks as if my computer has device nodes for $PROTO, and I suppose that's not the case. I think it would make more sense as (optional) /dev nodes giving all programs access to it.
The man page for bash says that /dev/tcp and /dev/udp actually can be real files that any program can use, but that if they're absent, it emulates the behavior on its own.
Hey I'm the author. Yes, as far as I know it's just sugar for opening a socket, and there's almost never a file that exists. To treat it as a file, you need to assign a file descriptor to it, which you can then read and write from.
Actually, I don't have that device in my normal shell, mksh, but the command works in bash? Is bash putting things in /dev? That seems rather... bad, to be honest.
Bash isn't putting anything in /dev. It checks to see if those files exist (I've never seen them actually exist though), and if they aren't there, it just parses the string and creates a network connection as though they did exist.
Bash is not even pretending such special files exist: /dev/tcp/somewhere is only an elegant syntactic shortcut for requesting a telnet-like network connected pipe in a context in which a filename is required.
For the child process, it's just a file descriptor.
The difference seems to be that zsocket works with Unix domain sockets and, only for incoming connections, sockets already opened and with a file descriptor. I suppose this latter one can be used for any kind of socket if opened before exec'ing zsh.
Hey I'm the author. Yeah, I stumbled on this while doing a little research into zsh socket capabilities, but I decided to exclude it because the post was more about network sockets than Unix domain sockets.
Yeah, when I originally wrote the comment I thought that it was more generic. It was only later that I saw it was more aimed at domain sockets, and edited that in.
You might be interested in looking into a fuse based filesystem like httpfs2¹ if you are looking for this sort of capability. The would allows you to mount http/https locations as a filesystem.
I know I'm preaching to the choir for this, but it's stuff like this that makes me absolutely adore Unix and its entire design. The fact that virtually everything has a file abstraction greatly simplifies a lot of work.
It was disabled at one point, but definitely enabled at the moment in buster at least (try docker run -it debian:buster-slim /bin/bash if you don’t have Debian at hand).
bash (4.0-5) unstable; urgency=low
* Re-add dependency on dash, lost with the upload of 4.0.
* Don't configure with --disable-net-redirections.
* Fix name of system wide bash_logout in bash(1). Closes: #546200.
* Stop shipping the sh and sh(1) symlinks. Closes: #545103.
* Apply upstream patches 029 - 033.
-- Matthias Klose <doko@debian.org> Sun, 13 Sep 2009 12:55:54 +0200
it seems net-redirections has been reenabled more than a decade ago.
Does anyone know the motivation for this in bash? A quick glance at the git history shows this goes all the way back to bash-2.04. As a grumpy old *nix person I can't really I like seeing extra "features" like this.
this is useful for network connectivity troubleshooting in any environment (container, embedded device, and so on) where curl, nc, telnet, etc. are not installed
It happens to work for the intended purpose, but not for the reasons the author intended.