February 27th 2018

linux, bash

Load bash builtin from file


A few years ago I noticed that sleep is not built into bash and instead calls an external command each time.

I like to make my scripts responsive and unobtrusive by relying on bash builtins as much as possible; my reasoning: I am using one of the heaviest shells with all its bells and whistles, so at least use it as fully as possible. Once the shell has fired up, a multiline string manipulation should still be much faster than a single call to grep or sed etc., and without reading from the hard drive.
My hard drives are the oldest and slowest parts inside my computer...

Anyhow, bash has a mechanism to load builtins from a "shared object", usually a file in /usr/lib*/bash/. See help enable.

Make it portable:

#!/bin/bash
for file in /usr/lib/bash/sleep /usr/lib32/bash/sleep /usr/lib64/bash/sleep; do
    [ -r "$file" ] && enable -f "$file" sleep && break
done
# Portable enough?

# a little test: 
delay=0.01 # 100 times per second
I=6000 # should amount to 1 minute
printf "Time for builtin loaded from $file:"
time ( for ((i=0 ; i<I; i++)); do sleep $delay; done )
enable -d sleep
printf "Time without builtin:"
time ( for ((i=0 ; i<I; i++)); do sleep $delay; done )

The time difference:

Time for builtin loaded from /usr/lib/bash/sleep:
real    1m0.899s
user    0m0.300s
sys 0m0.093s
Time without builtin:
real    1m14.334s
user    0m9.227s
sys 0m5.502s

Please keep in mind that bash builtins might have different functionality than the equivalent external utilities. In the case of sleep, the builtin is able to take fractions (just like the external), but always assumes seconds.

Here's a list of all builtins that come with my version of bash (4.4.19):

$ ls /usr/lib/bash/
Makefile.inc  finfo  ln       mypid    printenv  rmdir      strftime  truefalse  unlink
basename      head   logname  pathchk  push      setpgid    sync      tty        whoami
dirname       id     mkdir    print    realpath  sleep      tee       uname