Dit deel is nog niet vertaald. Je ziet de Engelse tekst.
$ als gewone gebruiker# als root (of met sudo)
Vaardigheden in dit hoofdstuk
- Bash-scripts schrijven met variabelen, tests en lussen
Write and run your first script
A script is a text file. The first line (#!/bin/bash, the shebang) says which program runs it. Make it executable and start it with its path.
#!/bin/bash
# Show a short report about this machine
echo "Host: $(hostname)"
echo "Kernel: $(uname -r)"
echo "Uptime: $(uptime -p)"
df -h /mkdir -p ~/bin
vim ~/bin/sysinfo.sh
chmod +x ~/bin/sysinfo.sh
~/bin/sysinfo.sh
sysinfo.sh
bash ~/bin/sysinfo.sh~/binis in thePATHof normal users on RHEL, sosysinfo.shworks from any folder. For all users, put scripts in/usr/local/bin.bash FILEruns a script without the execute right — handy to test.
Onthoud
Comments start with #. Write one line at the top about what the script does — you will thank yourself in a year.
Use variables and quotes
NAME=value sets a variable (no spaces around =). $NAME or ${NAME} reads it. Put variables in double quotes so spaces do not break them.
BACKUP_DIR=/srv/backup
echo "Backups go to $BACKUP_DIR"
FILE="my report.txt"
touch "$FILE"
echo "${BACKUP_DIR}/etc-$(date +%F).tar.gz"
/srv/backup/etc-2026-10-27.tar.gz
COUNT=$(ls /etc | wc -l)
echo "There are $COUNT entries in /etc"
echo $(( 7 * 6 ))
42
(( COUNT > 100 )) && echo "many"| Form | Meaning |
|---|---|
VAR=value | Set a variable for this shell |
export VAR | Also give it to programs started from this shell |
$(command) | The output of a command |
$(( 2 + 3 )) | Calculate with whole numbers |
${VAR:-default} | The value, or default when VAR is empty |
${VAR:?message} | Stop the script with a message when VAR is empty |
Let op
FILE=my report.txt (no quotes) tries to run the command report.txt. And rm $FILE without quotes deletes two files: my and report.txt.
Read the input of a script
The words after the script name are its positional parameters: $1, $2 … $# is their number, "$@" is all of them.
#!/bin/bash
# Create one user for every name given on the command line
if [ $# -eq 0 ]; then
echo "Usage: $0 NAME [NAME ...]" >&2
exit 1
fi
for name in "$@"; do
useradd "$name" && echo "created $name"
donemkusers.sh mia leo sara
created mia
created leo
created sara
read -p "Which host? " HOST
echo "You typed $HOST"| Variable | Meaning |
|---|---|
$0 | The name of the script |
$1 … $9 | Parameter 1 … 9 (${10} for more) |
$# | The number of parameters |
"$@" | All parameters, each one kept as its own word |
$? | The exit status of the last command |
$$ | The PID of the script |
Use exit codes
Every command returns a number: 0 = success, 1–255 = a problem. Your script should do the same with exit N, so other tools (cron, systemd, Ansible) know if it worked.
grep -q root /etc/passwd
echo $?
0
grep -q nosuchuser /etc/passwd
echo $?
1
ping -c1 -W1 server2 &> /dev/null && echo up || echo down#!/bin/bash
# Exit 2 when the root file system is more than 90 % full
USED=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if [ "$USED" -gt 90 ]; then
echo "CRITICAL: / is ${USED}% full"
exit 2
fi
echo "OK: / is ${USED}% full"
exit 0Test conditions
[ … ] (the test command) and [[ … ]] (Bash) check files, text and numbers. They return 0 (true) or 1 (false).
| Test | True when |
|---|---|
-e FILE / -f FILE / -d DIR | It exists / is a normal file / is a folder |
-r / -w / -x FILE | You may read / write / run it |
-s FILE | It exists and is not empty |
-z "$S" / -n "$S" | The text is empty / not empty |
"$A" = "$B" / != | Texts are equal / different |
N -eq M, -ne, -lt, -le, -gt, -ge | Number compare: =, ≠, <, ≤, >, ≥ |
[[ $S == web* ]] | Pattern match (only in [[ ]]) |
[[ $S =~ ^[0-9]+$ ]] | Regular expression match (only in [[ ]]) |
!, &&, || | Not, and, or |
[ -f /etc/hosts ] && echo "file exists"
[ -d /srv/backup ] || mkdir -p /srv/backup
test "$USER" = root && echo "you are root"
[[ $(hostname) == server* ]] && echo "a lab server"Onthoud
Spaces are needed inside the brackets: [ -f FILE ], not [-f FILE].
Choose with if and case
if runs a block only when a test is true; elif and else cover the other cases. case compares one value with many patterns.
#!/bin/bash
# Start, stop or show a service: svc.sh start|stop|status NAME
ACTION=$1
SERVICE=${2:?give a service name}
case "$ACTION" in
start|stop|restart)
systemctl "$ACTION" "$SERVICE" ;;
status)
if systemctl is-active --quiet "$SERVICE"; then
echo "$SERVICE is running"
elif systemctl is-failed --quiet "$SERVICE"; then
echo "$SERVICE FAILED"
exit 2
else
echo "$SERVICE is stopped"
fi ;;
*)
echo "Usage: $0 start|stop|restart|status SERVICE" >&2
exit 1 ;;
esacNotitie
if tests the exit code of any command — not only [ ]. if grep -q …, if ping -c1 …, if systemctl is-active --quiet … are all fine.
Repeat with for and while
for runs a block once for every item in a list. while runs it as long as a test is true — often to read a file line by line.
for h in server1 server2 server3; do ping -c1 -W1 $h &> /dev/null && echo "$h up" || echo "$h DOWN"; done
for f in /var/log/*.log; do echo "$f: $(wc -l < "$f") lines"; done
for i in {1..5}; do echo "test $i"; done
for u in $(cut -d: -f1 /etc/passwd); do echo "$u"; done | head -3
while read -r host; do ssh "$host" uptime; done < hosts.txt
n=0; while [ $n -lt 3 ]; do echo "n=$n"; n=$((n+1)); done#!/bin/bash
# Wait until a host answers ping (max. 60 tries), then go on
HOST=${1:?host name}
for try in $(seq 1 60); do
if ping -c1 -W1 "$HOST" &> /dev/null; then
echo "$HOST is up after $try tries"
exit 0
fi
sleep 1
done
echo "$HOST did not answer" >&2
exit 1Op het werk
Loops are not only for scripts. One for line at the prompt can check 20 servers in a few seconds.
Find bugs in a script
bash -x prints every line before it runs, with the values filled in. set -euo pipefail stops the script at the first error.
bash -x ~/bin/sysinfo.sh
+ hostname
+ echo 'Host: server1.example.com'
Host: server1.example.com
bash -n ~/bin/mkusers.sh#!/bin/bash
set -euo pipefail| Setting | Effect |
|---|---|
bash -n FILE | Check the syntax only, run nothing |
bash -x FILE / set -x | Trace: show each command before it runs |
set -e | Stop when a command fails |
set -u | Stop when an unset variable is used |
set -o pipefail | A pipe fails when any part fails |