I have a little bit of bash (see bottom) that basically checks to see if any box is pingable on the LAN. But with a trick: it'll quit/return the instant it gets a single reply. That's so my script doesn't have to wait for nmap to finish (which can take a minute vs maybe 1 sec for the first computer to respond).
It works great. One problem though... on one box only, sometimes (not always) I'll get a:
grep: write error: Broken pipe
in the output. I would like to suppress that error.
I ran bash with -x and the grep error appears in the debug output after the grep -q RCVD line... however I'm not sure that's not a red herring as the entire stanza may be counted as one line for the debug? And I fail to see how there can be a write error on that part of the pipe -- there's nothing it's piping to! A read error, sure. But write?
Yes, the kill makes huge swathes of the pipe just "go away", and maybe that's part of it. But I'm trying to redirect most 2>&1's as you can see.
I don't need to solve the "problem" of pipes disappearing, I just want it to shut up. I also want to solve it without putting the meat of the code into a separate script (that seems like cheating). Any ideas?
sint=192.168.100
timeout 20 bash -c \ " \ (nmap -ddd -vvv --send-ip \ --host-timeout 5s --max-rtt-timeout 1s --initial-rtt-timeout 500ms \ --max-retries 1 -S $sint.1 -sn -PE $sint.2 $sint.100-254 2>&1 & \ echo $! >&3) \ 3>pidnmap \ | grep --line-buffered -P 'RCVD.{0,30}?ICMP.{0,60}?Echo reply' \ | (head -1 && /bin/kill $(<pidnmap) >/dev/null 2>&1) \ " \ |grep -q RCVD || { # do some network-restart stuff }
I think you might be able to suppress the error output by redirecting the outer shell's stderr to /dev/null, but I'm struggling to see where either grep instance could possibly see its stdout vanish without warning! I'm not 100% convinced the error message is actually coming from grep, rather I think it might be coming from the shell instead.
I would also not use nmap for this, I suspect fping will be far better suited. It looks like you want to always, sending as x.x.x.1, ping x.x.x.2 + x.x.x.[100-254], and if nothing at all responds, kick the network in the head? If I've got that correct-ish, may I suggest:
env NET=172.31.31 /bin/sh -c 'fping -t 1 -X 1 -S $NET.1 -g $NET.0/24 >/dev/null && echo alive || echo dead'
replace the two terminal conditions with something more useful, obviously, likely a command list (use { }, not ( ) to avoid a needless fork!) that kicks networking in the head. Also I note that it doesn't matter if you use ' or " , it winds up the same command just evaluated by the parent vs the child shell. And if this is being run from cron, just make it a #!/bin/bash file, your life gets so much easier that way, mainly you stop worrying about quoting.
/var/spool/cron/root (or /etc/crontab, or whatever): * * * * * /root/check-networking.sh
/root/check-networking.sh: NET=172.31.31 fping -t 1 -X 1 -S $NET.1 -g $NET.0/24 >/dev/null || { # do a bunch of stuff }
fping is present by default on just as many systems as nmap (i.e. almost none) so I don't see that as a problem. Also its "-X" option appears to be *exactly* what you are trying to build with nmap. Although tbf I didn't know about the -x or -X options until 5 minutes ago... my example was way more complicated until then! (But still less complex than yours, which is, admittedly, a neat way to cudgel nmap over the head into doing what you want.) If you're running this non-stop, I would make it a script that sleeps for 60 sec in a loop forever, then turn that into a systemd unit for task supervision for auto-restart in case it dies for some reason:
/root/check-networking.sh: DELAY=60 NET=172.31.31 while sleep $DELAY; do fping -t 1 -X 1 -S $NET.1 -g $NET.0/24 >/dev/null || { # do a bunch of stuff }; done
Doing so avoids the cron log entry every 60sec, avoids the fork, and keeps running even if the system gets REALLY wedged due to getent(3) breaking in interesting ways due to network breakage. 'Cuz we've never seen modern Linux do that, never, ever... sigh. I actually miss Solaris. -Adam
-----Original Message----- From: Trevor Cordes trevor@tecnopolis.ca Sent: Saturday, December 27, 2025 10:59 PM To: MUUG RndTbl roundtable@muug.ca Subject: [RndTbl] suppress broken pipe error
I have a little bit of bash (see bottom) that basically checks to see if any box is pingable on the LAN. But with a trick: it'll quit/return the instant it gets a single reply. That's so my script doesn't have to wait for nmap to finish (which can take a minute vs maybe 1 sec for the first computer to respond).
It works great. One problem though... on one box only, sometimes (not always) I'll get a:
grep: write error: Broken pipe
in the output. I would like to suppress that error.
I ran bash with -x and the grep error appears in the debug output after the grep -q RCVD line... however I'm not sure that's not a red herring as the entire stanza may be counted as one line for the debug? And I fail to see how there can be a write error on that part of the pipe -- there's nothing it's piping to! A read error, sure. But write?
Yes, the kill makes huge swathes of the pipe just "go away", and maybe that's part of it. But I'm trying to redirect most 2>&1's as you can see.
I don't need to solve the "problem" of pipes disappearing, I just want it to shut up. I also want to solve it without putting the meat of the code into a separate script (that seems like cheating). Any ideas?
sint=192.168.100
timeout 20 bash -c \ " \ (nmap -ddd -vvv --send-ip \ --host-timeout 5s --max-rtt-timeout 1s --initial-rtt-timeout 500ms \ --max-retries 1 -S $sint.1 -sn -PE $sint.2 $sint.100-254 2>&1 & \ echo $! >&3) \ 3>pidnmap \ | grep --line-buffered -P 'RCVD.{0,30}?ICMP.{0,60}?Echo reply' \ | (head -1 && /bin/kill $(<pidnmap) >/dev/null 2>&1) \ " \ |grep -q RCVD || { # do some network-restart stuff } _______________________________________________ Roundtable mailing list -- roundtable@muug.ca To unsubscribe send an email to roundtable-leave@muug.ca
On 2025-12-28 Adam Thompson wrote:
I think you might be able to suppress the error output by redirecting the outer shell's stderr to /dev/null, but I'm struggling to see where either grep instance could possibly see its stdout vanish without warning! I'm not 100% convinced the error message is actually coming from grep, rather I think it might be coming from the shell instead.
That could very well be. The shell saying "grep is gonna have this problem, and I'm telling you"?
I would also not use nmap for this, I suspect fping will be far
Doh, now you tell me! :-)
Ya, fping does exactly what I need without all the tricks. I never knew it existed. It's in Fedora's repos so that's good enough for me.
I settled on: fping -q -r 1 -t 1 -X 1 -S ... { # clear! }
Some other options look promising but as soon as you start fiddling with counts and timeouts it switched into "run a lot, output a lot" mode for some reason, even with -q. I'll have to just assume the defaults for these things are sane.
Yup, run in an always-running script, started from systemd for autorestarts, runs every few mins and restarts things that seem busticated and systemd didn't/can't handle.
Yes, this one bit is to paddle-resuscitate the lan/nic should the 25+ hosts all suddenly disappear. Usually due to whacked out or dying NIC, or whacked out or dying switch(/port). Rarely hits, tries a fix, and notifies me to look into it.
As for the spurious pipe error, I'll safely ignore until I ever need to try that self-killing hack again!
Thanks!