One of the most useful features of shells is the ability to write scripts. It should not come as a surprise to find that there are many commands that only make sense when used with scripts.
[
and test
test(1) and [
are synonymous, apart from the fact that the
last argument to [
must be ]
.
They are programs for evaluating various expressions, usually used in shell flow control statements.
var=foo
if [ "$var" = foo ]; then
echo bar
fi
It is important to realise that [
is not a syntactical construct,
any command may be used in a shell conditional expression.
true
and false
Processes, as well as producing output, terminate with an "exit status". Sometimes the exit status is more important than the output. Occasionally the exit status is all you want.
Since the only two exit status codes that people generally care about are zero (ok) and non-zero (error), the true(1) and false(1) commands were written.
One major use for this is flow control in shell scripts.
if complicated and expensive command; then
ok=true
else
ok=false
fi
if "$ok"; then
echo I am ok
fi
Without true(1) or false(1) you would need to come up with your own convention for booleans. You would then need to use test(1) to check them, something like this:
if complicated and expensive command; then
ok=1
else
ok=0
fi
if [ "$ok" = 1 ]; then
echo I am ok
fi
Another is setting a user's shell to /bin/false
so they cannot log in.
$ grep false /etc/passwd
messagebus:x:102:104::/var/run/dbus:/bin/false
A third is to replace a command that is no longer required, but other scripts expect to be able to run it.
For example, on old Linux systems, pre-devtmpfs and udev, there was a
script called /dev/MAKEDEV
. For compatibility, some systems keep the
script as a symlink to /bin/true
.
A final use, is when you need to have a command in that context.
For example, bash has variable substitution options that will set a variable to a default value. They need to be run as part of another command, or they will be interpreted as a command.
$ true ${var=default}
To make this less confusing, bash introduced :
as a synonim for
true(1).
$ : ${var=default}
basename
and dirname
basename(1) and dirname(1) are used for manipulating file paths.
$ var=/path/to/foo/bar-file.txt
$ # <--dirname--><-basename->
$ # suffix-^--^
$ dirname "$var"
/path/to/foo/
$ basename "$var"
bar-file.txt
$ basename "$var" .txt
bar-file
They are mostly wrappers around the C library's basename(3) and dirname(3) functions, with the exception of basename(1) also taking a suffix parameter, which strips the suffix off the end of the file path if provided.
seq
and yes
seq(1) and yes(1) both produce lines of output based on their parameters.
seq(1) produces lines of integers in the provided range.
$ seq 3
1
2
3
$ seq 3 5
3
4
5
$ seq 10 10 30
10
20
30
$ seq 100 -20 60
100
80
60
This can be useful for loop conditions.
$ for i in `seq 1 3`; do echo "hello $i"; done
hello 1
hello 2
hello 3
Though in this case, bash brace expansion is more effective.
$ for i in {1..3}; do echo "hello $i"; done
hello 1
hello 2
hello 3
yes(1) prints the same line endlessly. You could naievely implement
it as yes(){ echo "${1-y}"; };
This may not sound useful for anything more than heating the area immediately around your computer, but there are often commands that ask for confirmation over standard input.
This may be an installer script, where the defaults are fine, or you
may have a version of cp(1) that doesn't have a --no-clobber
or
--force
option, but does have an --interactive
option, which can
be simulated with yes(1) as yes n | cp -i "$source" "$dest"
and
yes | cp -i "$source" "$dest"
respectively.
timeout
and nohup
timeout(1) and nohup(1) alter a command's termination conditions.
timeout(1) can be used to end a process after a given period of time. For example, you can work out how many lines yes(1) can print in 1 second with:
$ timeout 1s yes | wc -l
8710114
nohup(1) will stop a process being killed when the terminal it runs
from is closed. This is not demonstrable in the medium I have chosen to
demonstrate commands with, but it is run as nohup COMMAND >output
.
If the output of this command is not redirected away from the terminal,
then it is written to nohup.out
.
This is useful if you have a long-running job that you do not want to end when you disconnect from the server you are running it on.
However, screen(1) is a better choice for this task, as it gives
you a terminal you can detach from by pressing ^A-D
and reattach later
with the screen -x
command.
sleep
sleep(1) exits after the specified period. You can use it to implement timeout(1) as:
timeout(){
time="$1"
shift
"$@"&
pid="$!"
sleep "$time"
kill "$pid"
}
It is also useful in a retry loop to avoid busy-waiting.
while ! ssh some-server; do
sleep 10
done
tr
tr(1) is used to replace or delete characters in a stream.
For example, this will make all characters upper-case.
$ echo hello world | tr '[:lower:]' '[:upper:]'
HELLO WORLD
The -d
flag can be used to remove characters from a stream. This will
remove all vowels:
$ echo hello world | tr -d 'aeiou'
hll wrld
printf
printf(1) is mostly a thin wrapper over printf(3). Look to that for most of the details.
One addition worth mentioning is that bash has a built-in version,
which also supports %q
similarly to %s
, but quoted so you can use
it to safely generate scripts.
$ printf 'echo %q %q' "foo bar" "baz"
echo foo\ bar baz
tee
tee(1) writes its standard input to its standard output and any files listed on the command line.
$ echo foo bar | tee out
foo bar
$ cat out
foo bar
It is also usable as a way to write the output of a command somewhere you don't usually have permission to write, in conjunction with sudo(1).
$ echo foo bar | sudo tee out >/dev/null
Beware that many of these commands are actually shell builtins (or more precisely, they can be both a shell builtin and also a command in /bin or /usr/bin). When you run
true
in a script, you're not running true(1) from/bin/true
, you're running the builtintrue
. There's a performance difference there but it's not a big issue. When it comes to other commands like[
andprintf
, there can be a big difference and you should look athelp [
rather thanman [
for documentation.Use
type
to investigate what a command actually is:(don't ask about that last one!)