Handy Shell Snippets
Wait for a tcp port
Wait for a tcp port is open and accepting connections. You may want to combine this with a timeout and with a fail fast.
$ wait_port() {
> while ! nc -z 127.0.0.1 $1 >/dev/null 2>&1; do sleep 0.5; done
> }
$ wait_port 80 # byexample: +fail-fast +timeout=5 +skip
Ignore the
+skip
option.
If instead you want to pick a free port do something like this:
$ free_port() {
> for port in {1500..65000}; do ss -tln | grep -q ":$port " || echo "Port $port" && break; done
> }
$ free_port # byexample: +fail-fast +unless=on-macos
Port <port>
Like before you may want to combine this with a fail fast option and you can use the capture and paste functionality to save and use the port later without parsing the output yourself.
Note:
ss
is an “utility to investigate sockets”. You may use the oldernetstat -tan
of the same purpose.
Lock a file
Use flock
to synchronize your programs and avoid a race condition.
Combine this with a fail fast to fail quickly if the lock cannot be obtained and with a skip to make your that sure unlock the file at the end:
$ # try to get the lock, fail fast if we cannot
$ exec {fd}>>test/ds/f && flock -n $fd || echo "Lock failed" # byexample: +fail-fast +unless=on-macos
$ # your code here
$ # release the lock, do not skip these steps to avoid deadlocks
$ flock -u $lockfd # byexample: -skip +pass +unless=on-macos
$ exec {lockfd}>&- # byexample: -skip +pass +unless=on-macos
$ rm -f test/ds/f # byexample: -skip +pass
Keep tracking a log
You have a program that logs something of your interest asynchronously.
Use tail -f
(or tailf
) to keep track of
the log file in the background, only then run your program and
lastly bring the tail
back to the foreground to do the check.
Here we do the first part using +stop-on-silence to send it to the background:
$ tail -f test/ds/some.log # byexample: +stop-on-silence
Then we run the asynchronous command (here you put your command)
$ (sleep 0.5 ; echo 'very important message!' >> test/ds/some.log) &
[<job-id>] <pid>
And finally, we bring back the tail
and check. We extend the
timeout
to give the echo
an opportunity to complete and log.
$ fg %1 # byexample: +stop-on-timeout +timeout=1
tail -f test/ds/some.log
very important message!
Did you notice the difference between
+stop-on-silence
and+stop-on-timeout
? The former sends the program to the background ifbyexample
does not detect any output from it after a small fraction of time (aka silence). The latter does the same but when the timeout is over.
Because fg %1
will never end we need to
send it the background again and not fail with a timeout (that’s why
we use +stop-on-timeout
).
To finish it, we can kill it like any other process. You typically do not want to skip this.
$ kill %% ; fg ; wait # byexample: -skip
tail -f test/ds/some.log
Terminated<...>
No POSIX-conformant Bash
By default, byexample
uses bash
in POSIX-conformant mode.
If you execute an shell example and you get a syntax error, you may be using a non-POSIX syntax.
You can disable the POSIX-conformant from within bash
with set
+o posix
:
$ echo $POSIXLY_CORRECT # this Bash's variable says yes if we are in POSIX
y
$ set +o posix
$ echo $POSIXLY_CORRECT # we are not longer in POSIX mode, happy hacking
$ set -o posix
$ echo $POSIXLY_CORRECT # back to the default of byexample
y