BASH Programming

Bash builtin examples

builtin candidate_builtin arg … in bash allows you to only call bash builtins. That is, even if an external command or function exists also named candidate_builtin (or something more creative), bash will ignore it and only try to run the builtin along with its args.

With builtin, you can be sure that you are running a bash builtin instead of some external command or function that just happens to be floating around in your environment waiting to be called by a bash programmer. However, if someone overrides builtin as a function, then you may want to have it unset.

Similar to the way you would want to change the default behavior of an external command like curl in bash, builtin allows you to work around the case that you have declared a function with the same name as declare.

In addition to allowing you to run builtin commands directly, it can be used to test if a name is a builtin.

builtin builtin

Builtin is a builtin. Great! How about this?

builtin builtinf

As you might have guessed, the above builtin command line returns a non-zero exit status, something bad happened.

bash: builtin: builtinf: not a shell builtin

Okay, builtinf is not a builtin. Maybe in a future version of bash when it has a builtin called builtinf.

If you are still not sure how to use builtin or want to see how the help page looks, this is for you.

Builtin help

Glancing through the help page for builtin is a good place to start if you are interested in learning how builtin works or need review.

$ help builtin
builtin: builtin [shell-builtin [arg ...]]
Execute shell builtins.
Execute SHELL-BUILTIN with arguments ARGs without performing command
lookup.  This is useful when you wish to reimplement a shell builtin
as a shell function, but need to execute the builtin within the function.
Exit Status:
Returns the exit status of SHELL-BUILTIN, or false if SHELL-BUILTIN is
not a shell builtin..

You should have a basic understanding of how builtin may be used in a bash script by now. Let’s dive into some examples.

Examples using builtin

Before we start any examples let’s decide if we need builtin with a quick checklist. That is, if any of the following statements are true you may need builtin.

  1. You’ve defined a function with the same name as a builtin listed in the list of bash builtins in an attempt to extend the default builtin behavior
  2. You want to explicitly call a builtin by name to prevent calling a function with the same name unintentionally
  3. You want to check if a name is a builtin to avoid naming a function with the same name as a builtin

Examples using builtin to extend the default behavior of any builtin follows.

Builtin example: 1 unalias to rule them all (unalias)

At the very bottom of the lists of bash builtins there is a builtin named unalias, which is like unset for aliases. Suppose that you want unalias to ignore any arguments and simply throw away any alias in the environment, rule them all. It turns out unalias has that option. Also, it would become redundant if you had to type it every time so let’s through it all into a function. Here’s how.

Commands

unalias() {
builtin ${FUNCNAME} -a
}

Test

alias flat='echo flat'
flat
unalias
flat

Output

flat
bash: flat: command not found
Footnotes

You may argue that we could get away with using an alias instead of a function. You could but the result would be a once-function, i.e. unalias would revert to its default behavior after one call.

Builtin example: 2 echo with or without color (echo)

Somewhere close to the middle of the list of bash builtins, there is a builtin named echo. Maybe you have heard of it. I have a hunch that you have. However, you may not have heard of cecho.sh, a script that I wrote to echo with color. No worries. We’re going to do something similar in this example using builtin to call echo.

Commands

echo() { { local candidate_color ; candidate_color="${1}" ; local line ; line="${@:2}" ; }
echo-color() {
case ${candidate_color} in
blue) builtin echo 34 ;;
yellow) builtin echo 33 ;;
green) builtin echo 32 ;;
*) builtin echo 0 ;;
esac
}
builtin echo -n -e "\e[$( ${FUNCNAME}-color )m${line}\e[0m"
}

Test

echo yellow asdf  ; echo green asdf asdf  ; echo white asdf

Output

asdfasdf asdfasdf
Footnotes

Based on cecho.sh. You may add other colors in the echo-color function. You may modify the last builtin echo line to fit your desired default behavior or option handling scheme for echo.

Builtin example: 3 just a command (command)

command is a builtin. Like builtin it, allows us to control whether a function, external command, or builtin is called in the case that more than one shares the same name. Unlike builtin, command executes external commands and builtins, which is the same as anything that isn’t a function. What if we want to remove builtins from command? That is where builtin comes into play.

Note that builtins have higher precedence than external commands. That is, the interpreter will check for a builtin before looking for an external command.

Consider the case of echo.

It is both a builtin and an external command. However, when we run

command echo something

The echo builtin will be used. How do we know?

If you run which echo, you would see something like /usr/bin/echo. Furthermore, /usr/bin/echo –help displays a man page, where the builtin echo doesn’t. So we run

command echo --help
And get back
 
--help

Thanks for the help echo. At least now we know that builtins run before external commands.

Now let’s see how we can use builtin to modify command to only run external commands for fun.

Commands

command() {
! which ${1} || { $( which ${_} ) ${@:2} ; return ${?} ; }
builtin ${@}
}

Test

command echo --help # now displays a man page

Here we showed how to modify the command builtin to only try external commands. Next, let’s try fun like make it hard unset variable and functions.

Builtin example: 4 disable unset (unset, builtin)

To disable unset we need to override unset and builtin using a function. In this example, we will show you how.

Commands

builtin() { echo fake builtin ; }
unset() { echo fake unset ; }

Now that we have the function we need to override unset and builtin, it is time to test that our solution works. Here we go!

Test 0: behavior in most settings
{
foo=bar
echo ${foo} # bar
unset foo
echo ${foo} #
builtin unset foo
echo ${foo} #
}
 
Test 1: with unset disabled
 
{
builtin() { true ; }
unset() { true ; }
foo=bar
echo ${foo} # bar
unset foo
echo ${foo} # bar
builtin unset foo
echo ${foo} # bar
}

Note that in addition to overriding unset with a function we need to override builtin as well to prevent a command to bypass our unset function.

Bash example: 5 source with include path (source)

Source is a bash builtin that lets you include another file in your script and run with arguments. What if we want to change the source directory to some location other than our working directory. It can be done. Here’s how!

Commands

source() {
builtin source ${source_dir-.}/${1}.sh ${@:2}
}
 
Test
 
{
source() {
builtin source ${source_dir-.}/${1}.sh ${@:2}
}
source_dir=sources
test -d "sources" || mkdir -pv ${_}
echo "a() { echo a; } ; a" > ${source_dir}/a.sh
echo "b() { echo b $( a ) ; } ; b" > ${source_dir}/b.sh
echo "c() { echo c $( b ) ; } ; c" > ${source_dir}/c.sh
source a
source b
source c
}

Output

a
b a
c b a

Here we showed that you can roll your own source command. I recommend you put something like it in your boilerplate if want a customize source to reduce keystrokes in your bash scripts.

Builtin example 6: builtin -p to list bash builtins

Hey, maybe you are sick and tired of having to look up bash builtins. As you know other commands allow you to list options using the -p option. Unfortunately, builtin doesn’t provide that option yet.

Script
 
#!/bin/bash
## test-builtins
## version 0.0.1 - initial
##################################################
builtins() {
cat << EOF
alias
bind
builtin
caller
command
declare
echo
enable
help
let
local
logout
mapfile
printf
read
readarray
source
type
typeset
ulimit
unalias
EOF

}
generate-disable-builtins-script() {
local builtin
test ! -f "script" || rm -v ${_} 1>/dev/null
for builtin in $( builtins )
do
echo "${builtin}() { true ; }" >> script
done
}
test-builtins() {
generate-disable-builtins-script
}
##################################################
if [ ${#} -eq 0 ]
then
true
else
exit 1 # wrong args
fi
##################################################
test-builtins
##################################################
## generated by create-stub2.sh v0.1.2
## on Wed, 28 Aug 2019 13:45:00 +0900
## see <https://github.com/temptemp3/sh2>
##################################################

Source test-builtins.sh

Commands

{
curl https://raw.githubusercontent.com/temptemp3/ba.sh/master/test-builtins.sh
 -O --silent
. test-builtins.sh
. script
rm ${_}
declare -p
}

Output

# silence is golden

Here we showed how you can disable all builtins using functions. For the output of our proposed builtin -p command, just run builtins from test-builtins.sh. Full implementation will be left to the reader as an exercise.

Commands

{
curl https://raw.githubusercontent.com/temptemp3/ba.sh/master/test-builtins.sh
 -O --silent
. test-builtins.sh
builtin() { test ! "${1}" = "-p" || { builtins ; return ; } ; builtin{,} ${@} ; }
builtin -p
}

Output

alias
bind
builtin
caller
command
declare
echo
enable
help
let
local
logout
mapfile
printf
read
readarray
source
type
typeset
ulimit
unalias

Builtin example: 7 disable listing of aliases

Suppose in a restricted shell somewhere out there in the bash universe, the listing of aliases is disabled. Here may be how you could disable the listing of aliases using functions.

Commands

alias ()
{
test ! "${1}" = "-p" || {
true;
return
};
builtin alias ${1}="${@:2}"
}
 
Test
 
{
alias bash "echo bad bad bash"
alias -p bash
bash
}

Output

bad bad bash

Builtin example: 8 using enabled

As you recall, we disabled builtins using functions above. It turns out that you can get the same work down using the enable builtin. Here’s how.

Commands

disable() {
builtins() { enable | cut '-d ' '-f2' ; }
local builtin
for builtin in $( builtins )
do
test ! "${builtin}" = "enable" || continue
test ! "${builtin}" = "continue" || continue
test ! "${builtin}" = "local" || continue
test ! "${builtin}" = "shopt" || continue
test ! "${builtin}" = "shift" || continue
test ! "${builtin}" = "return" || continue
test ! "${builtin}" = "read" || continue
echo "disabling ${builtin} ..."
enable -n ${builtin}
done
}
 
Test
 
{
disable
enable
}

Output

disabling . ...
disabling : ...
disabling [ ...
disabling alias ...
disabling bg ...
disabling bind ...
disabling break ...
disabling builtin ...
disabling caller ...
disabling cd ...
disabling command ...
disabling compgen ...
disabling complete ...
disabling compopt ...
disabling declare ...
disabling dirs ...
disabling disown ...
disabling echo ...
disabling eval ...
disabling exec ...
disabling exit ...
disabling export ...
disabling false ...
disabling fc ...
disabling fg ...
disabling getopts ...
disabling hash ...
disabling help ...
disabling history ...
disabling jobs ...
disabling kill ...
disabling let ...
disabling logout ...
disabling mapfile ...
disabling popd ...
disabling printf ...
disabling pushd ...
disabling pwd ...
disabling readarray ...
disabling readonly ...
disabling set ...
disabling source ...
disabling suspend ...
disabling test ...
disabling times ...
disabling trap ...
disabling true ...
disabling type ...
disabling typeset ...
disabling ulimit ...
disabling umask ...
disabling unalias ...
disabling unset ...
disabling wait ...
enable continue
enable enable
enable local
enable read
enable return
enable shift
enable shopt

Here we showed you how to disable (most) builtins using the enable builtin. You may opt to disable the rest at the end of the loop.

Bottom line

Builtin helps modify the shell behavior in bash. Here we showed a few ways that buitlin may be used such as to disable builtin command features or the commands entirely. Still, there are untouched topics such as loading new builtin commands through dynamic loading that may be covered at a later date.

About the author

Nicholas Shellabarger

Nicholas Shellabarger

A developer and advocate of shell scripting and vim. His works include automation tools, static site generators, and web crawlers written in bash. For work he tools with cloud computing, app development, and chatbots. He codes in bash, python, or php, but is open to offers.