Bash-Scripting
Siehe auch
Shell-Skripte werden im Grunde genommen so erstellt: die Befehle, die man auf der Kommandozeile eingeben würde, landen in einer Text-Datei, die ausführbar gemacht wird. Wichtig ist noch, unter der bash
-Shell den Umgang mit Variablen und Schleifen zu üben sowie die erste Zeile des Shell-Scriptes zu kennen - den Shebang, beispielsweise #!/usr/bin/env bash
. Die Dateiendung eines Skriptes spielt keine Rolle, da der Shebang den vollen Pfad zum Kommandozeilen-Interpreter enthält. So lassen sich auch PHP-, Perl, Python- und andere Skripte elegant umsetzen, natürlich mit angepasster Syntax. Wird der Shebang weggelassen, nimmt CentOS an, dass es sich um ein Bash-Skript handelt.
Achtung
In Dateien mit UTF-8 BOM (Byte Order Mark) funktioniert der Shebang nicht. Grund: Der Shebang muss in den ersten zwei Bytes der Datei auftauchen.
Das Bash-Skript gilt damit als vollwertiges Programm und kann als solches im Betriebssystem aufgerufen werden. Bei Skripten mit Shebang werden die SUID- und SGID-Flags vom Linux-Kernel ignoriert.
Das Kapitel behandelt Shell-Scripting, im speziellen unter Bash. Es wird aber nicht auf Besonderheiten hingewiesen oder Unterschiede zu anderen Shell-Interpretern behandelt.
Beispiel: wer das Kommando find . -name "*.c" -ls
ausführt, erreicht das gleiche mit folgendem Shell-Script:
- Distributionsübergreifendes Skript
#!/bin/bash find . -name "*.c" -ls
- Auf CentOS 7+ hin optimiert
#!/usr/bin/env bash find . -name "*.c" -ls
Ein anderes minimales, typisches Einführungs-Beispiel:
#!/usr/bin/env bash
myvar='Hallo Welt!'
echo $myvar
Die Dateiendung ist optional. Die Datei ausführbar machen und aufrufen:
chmod +x hello.sh
./hello.sh
Tipp
Shell-Skripte, deren Kommandos (z.B. Löschen von Dateien und Ordnern) von positiven Ergebnissen vorangegangener Kommandos abhängen, sollten immer mit einem set -e
beginnen. Im Beispiel wird die Ausführung des Skriptes abgebrochen, falls do-something
schiefgeht, und es kommt nicht mehr zu delete-something
:
#!/usr/bin/env bash
# -e Exit immediately if a command exits with a non-zero status
set -e
do-something
delete-someting
Es gibt neben der Bash eine ganze Reihe verschiedener Shells mit unterschiedlichen Eigenschaften und unterschiedlicher Syntax. Welche unter RHEL verfügbar sind, zeigt einem cat /etc/shells
.
Eine Liste aller internen Bash-Befehle erhält man mit help
. Mehr Informationen zu einem Kommando liefert help command
, zum Beispiel help if
.
Eine Quelle der Inspiration sind die bereits installierten Shell-Skripte, die mit find / -name *.sh
gefunden werden können. Ein kompaktes, schönes Beispiel ist /etc/profile.d/lang.sh
, welches die wichtigsten Skriptsprachen-Elemente wie if
, for
, case
usw. verwendet.
- Links
Ein Shell Style Guide mit Naming Conventions: https://google.github.io/styleguide/shellguide.html
ShellCheck: Ein Linter, findet auch Bugs in Shell-Skripten: https://www.shellcheck.net/
Syntax und Struktur
Beispiel für ein kleines Bash-Skript mit Shebang, verketteten Anweisungen und Kommentaren:
#!/usr/bin/env bash
# the above is the "Shebang"
# but these two lines are just a comment
# span a command over two lines with "\"
scp /path/to/my/file \
root@server:/tmp
# execute commands on the same line, but...
# ...execute all, even if one fails
cd /home/linus; ls -la
# execute commands on the same line, but...
# ...abort subsequent commands if one fails
make && make install && make clean
# execute commands on the same line, but...
# ...proceed until something succeeds and then stop
cat server.log || cat messages || cat server.1.log
# work with variables only within my shell script (the current shell)
myvar=This is a test.
echo $myvar
# set variable for current shell and all processes started from current shell
export myvar=This is a test.
echo $myvar
# indicate that everything was fine before
# leaving this script
exit 0
# indicate that an error occurde
# by returning any value > 0
exit 1234
Return-Werte / Exit-Codes eines Kommandos oder Skriptes lassen sich aus der Variablen $?
auslesen. $!
enthält die Prozess-ID des zuletzt ausgeführten Background-Prozesses (Pipeline).
In welchem Fall müssen welche Zeichen mit einem vorangestellten Backslash (\
) maskiert / gequotet / escaped werden, weil sie sonst in einem Bash-Script interpretiert werden?
# if not using any quotes, you have to quote:
Space $ " & | ( ) ' ` $ ; > < \
# if using single quotes, you have to quote:
nur der Single Quote muss gequoted werden: '\''
# if using double quotes, you have to quote:
" ` \
Variablen
Konvention und Scope
Konstanten werden in der Regel GROSS geschrieben, Variablen klein. a
ist eine andere Variable als A
. Der Google Bash Coding Shell Style Guide fasst einige Empfehlungen schön zusammen.
myvar="myvalue"
echo $myvar
Damit die Variable auch in Umgebungen aller Kind-Prozesse (z.B. Shell-Scripte, die in Bash gestartet werden) zur Verfügung steht, muss sie exportiert werden:
export myvar="myvalue"
bash
echo $myvar
Strings
Die erste Position in einem String beginnt bei Null (0).
# not using any quotes
myvar=StringWithoutSpaces,interpreted
echo $myvar
# using single quotes
myvar='String with Spaces, non-interpreted: $PWD'
echo $myvar
# using double quotes
myvar="String with Spaces, interpreted: $PWD"
echo $myvar
length, len, strlen:
mystrlen=${#mystring}
trim, strip:
mystr=$(echo $mystr | xargs)
mid, left, right, substr:
# substring from pos 2, 5 chars
mysubstr=${mystring:2:5}
# cut the first two chars
mysubstr=${mystring:2}
# cut the last four chars
mysubstr=${mystring:0:-4}
# get the last four chars
mysubstr=${mystring: -4}
# or shorter
mysubstr=${mystring::-4}
# everything to the first "a"
mymatch=${mystring#*a}; echo $mymatch
# from the beginning to the first "-"
mymatch=${mystring%-*}; echo $mymatch
in, instr:
# instr
if [[ $mystring == *"is a"* ]]; then
echo "It's there!"
fi
explode, split - alle Werte eines Strings mit Delimiter „,“ in ein Array packen:
IFS=',' read -r -a ARRAY <<< "$STRING"
# split a string by delimiter ":" and just get the third column
ps -eZ | grep httpd | cut -d':' -f3
cut, search and replace, Suchen und Ersetzen:
# cut the extension ".md" in a filename
echo "${file%.md}"
# find and replace the first occurence od 'dev' with 'DEV' in variable DISKS
DISKS=${DISKS/dev/DEV}
# find and replace ALL occurence of 'dev' with 'DEV' in variable DISKS
DISKS=${DISKS//dev/DEV}
Strings verketten (concat):
foo="Hello"
foo="$foo World"
a='hello'
b='world'
c=$a$b
# concate variable with a string
a='hello'
c=${a}world
Die Ausgabe zweier Befehle verketten:
{ command1 & command2; }
{ cat /tmp/needs-restarting & grep $(date +"%b ") /var/log/yum.log; }
Verwendet man $TEST
, und man möchte $TEST_1234
ausgeben (also den Inhalt der Variablen TEST, gefolgt von „_1234“), muss man wie folgt substituieren:
echo ${TEST}_1234
# falsch wäre "echo $TEST_1234" - so sucht Bash nach der Variablen "$TEST_",
# da "$TEST_" ein gültiger Variablenname ist
Interessanter Fall:
MSG="Automatic reboot now."
echo $MSG
MSG=""Automatic reboot now.""
echo $MSG
Letzteres führt zu einem Reboot (man kann auch andere beliebige Kommandos angeben). Der Hintergrund: ein Paar Double Quotes heben sich gegenseitig auf - sie sind faktisch gar nicht vorhanden. Der Befehl ist also wie folgt zu lesen: setze die Environment-Variable MSG auf „Automatic“ und führe „reboot now.“ aus - genau wie in EDITOR=nano crontab -e
.
Arrays
NAMES=(alice bob)
Anzahl Elemente eines Arrays:
echo "${#array[@]}"
Letztes Element eines Arrays:
echo "${array[-1]}"
Magic Vars
Die Parameter, die dem Skript übergeben werden, sind in $1
, $2
usw. abrufbar. $0
enthält den Dateinamen des Skriptes, inklusive des Pfades. $*
enthält alle Parameter in einem String, $@
(früher wurde hier das sicherheitskritische $**
verwendet) aufgetrennt nach Wörtern (nach Double-Quotes), $#
die Anzahl der übergebenen Parameter.
Benutzereingaben
#!/usr/bin/env bash
echo 'Ziel-Pfad für Installation: '
read installpath
echo $installpath
Ausgaben
Zeilenumbrüche und Tabulatoren ausgeben:
echo -e "Hello\tWorld\nSee\tme"
Zeilenumbruch bei der Ausgabe mit echo
verhindern:
echo -n "Prevent line break..."
echo "...and continue."
Mit Hilfe eines Bash-Skriptes einen mehrzeiligen Text in eine Datei schreiben:
cat > /path/to/myfile <<EOF
Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
...
EOF
.. note::
Das Ende-Token (hier mit dem Namen ``EOF``) darf beispielsweise in Schleifen nicht eingerückt werden, sonst wird es nicht gefunden. Falls man doch einrücken möchte, muss stattdessen ``<<-`` verwendet und mit Tabs eingerückt werden.
Funktionen
show_help () {
echo "Usage: $0 [OPTION]..."
echo "This tool makes ..."
echo "Function parameters are: $1, $2 and $3."
}
show_help "abc def" 'ghi' jkl
Kontrollstrukturen
if
Die if
-Anweisung kompakt:
if [ condition ]; then cmd1; cmd2; fi
Ausführlicher und besser lesbar:
if [ condition ]; then
cmd1
cmd2
elif [ condition ]
cmdA
cmdB
else
default-cmd
fi
So geht’s auch:
if [ condition ]
then
cmd1
cmd2
elif [ condition ]
cmdA
cmdB
else
default-cmd
fi
then
wird ausgeführt, wenncondition
einen Exit-Code von Null ergibt.else
wird ausgeführt, wenncondition
einen Exit-Code ungleich Null ergibt.
if in der Kurzschreibweise:
[ condition ] || cmd
# cmd wird nur bei condition return code != 0 ausgeführt
[ condition ] && cmd
# cmd wird nur bei condition return code = 0 ausgeführt (wird als "if true then cmd" gelesen)
[ condition ] && ( then_statement ) || ( else_statement );
Aufpassen bei der Verwendung der Klammerkonstrukte:
if [ condition ]
Ist eine andere Schreibweise für den POSIX-Befehl
test
, beispielsweiseif test ! -s "$1"
, funktioniert also unter jeder Shell. Diese Variante sollte bevorzugt eingesetzt werden.
if [[ condition ]]
Die moderne Variante, übernommen aus ksh, unterstützt in bash, zsh, yash und busybox. Kann zustzlich auf String-Wildcards prüfen.
if (( condition ))
Aus ksh übernommen, durch bash und zsh unterstützt, für arithmetische Operationen. Liefert einen Exit-Code von Null (true), wenn der ausgerechnete Wert ungleich Null ist.
if ( condition )
Startet eine Subshell.
Die Leerzeichen vor und nach den Klammern sind Pflicht. Siehe dazu auch https://unix.stackexchange.com/questions/306111/what-is-the-difference-between-the-bash-operators-vs-vs-vs
Eine Verkettung von Bedingungen wird korrekterweise wie folgt angwendet:
if [ $? -eq 4 -o $? -eq 8 ] ; then
....
fi
Das nachfolgende Beispiel ist falsch, denn der linke OR-Teil ändert den Rückgabewert (also $?
), so dass der rechte OR-Teil nicht den originalen Wert verwenden kann:
if [ $? -eq 4 ] || [ $? -eq 8 ] ; then
....
fi
Check auf Return-Codes von Funktionen:
# Shell
if func; then ...
# modern (e.g. Bash)
if [[ $(func; echo $?) -eq 0 ]]; then ...
# if you want to do a numeric test
if [ $(func) -ne 9 ]; then ...
# string check
if [ "$(func)" == "b" ]; then ...
case
case [ condition ] in
patt1)
cmd1
cmd2
;;
patt2)
cmdA
cmdB
;;
patt3)
...
*)
default-cmd (or nothing)
;;
esac
Case-Beispiel mit String-Matching anhand regulärer Ausdrücke:
case $1 in
a*)
# anything starting with "a"
cmd1
;;
b?)
# 2-chars starting with "b"
cmd2
;;
c[de])
# matches "cd" or "ce"
cmd3
;;
me?(e)t)
# "met" or "meet"
cmd4
;;
@a|e|i|o|u )
# matches one vowel
cmd5
;;
esac
Boolesche Operatoren
Verkettung von Conditions
Booleans:
und:
-a
oder:
-o
nicht:
!
Beispiel:
if [ $var1 -ne 0 -o $var2 -ne 0 ]; then
....
fi
cd /etc/yum.repos.d
if [ -e ovirt-4.2-pre.repo -a -e ovirt-4.2.repo ]; then
rm -f ovirt-4.2.repo
fi
Im Beispiel soll eine Variable auf einen Wert zwischen 2 und 5 geprüft werden (Bereichsprüfung):
if [ NOT [ $MYNUMBER -ge 2 -a $MYNUMBER -le 5 ]]; then
echo "Not in range.";
fi
Tipp
man test
hilft bei Details.
Dateien und Verzeichnisse
Datei- und Verzeichnisprüfungen (Hilfe erhält man hier mit man 1 test
):
if [ -d /tmp ]
then
echo "Directory /tmp exists."
fi
if [ -e /var/log/messages ]
then
echo "File /var/log/messages exists."
fi
if [ -f /var/log/messages ]
then
echo "File /var/log/messages is a regular file."
fi
if [ -r /var/log/messages ]
then
echo "File /var/log/messages is readable."
fi
if [ -s /var/log/messages ]
then
echo "File /var/log/messages size is > 0."
fi
if [ -w /var/log/messages ]
then
echo "File /var/log/messages is writeable."
fi
if [ file1 -nt file2 ]
then
echo "file1 is newer than file2."
fi
if [ file1 -ot file2 ]
then
echo "file1 is older than file2."
fi
POSIX test
functions in der Übersicht:
-a file exists.
-b file exists and is a block-special file.
-c file exists and is a character-special file.
-d file exists and is a directory.
-e file exists.
-f file exists and is a regular file.
-G file exists and is owned by the effective group ID.
-g file exists and its SGID bit is set.
-h file exists and is a symbolic link.
-k file exists and its sticky bit is set.
-L file exists and is a symbolic link.
-N file exists and has been modified since it was last read.
-n string is not empty.
-O file exists and is owned by the effective user ID.
-p file exists and is a named pipe (FIFO).
-r file exists and is readable.
-s file exists and has a size greater than zero.
-S file exists and is a socket.
-t file descriptor FD is open and refers to a terminal.
-u file exists and its SUID (set user ID) bit is set.
-w file exists and is writable.
-x file exists and is executable.
-z string is empty.
Strings
Für String-Vergleiche folgende Operatoren verwenden:
Gleichheit:
==
Ungleichheit:
!=
Grösser als:
>
Kleiner als:
<
Null, Länge = 0:
-z
Not Null:
-n
„<“ und „>“ müssen im „[ … ]“-Konstrukt gequotet werden:
PHP_VERSION=$(php -v)
if [ "$PHP_VERSION" \< "PHP 5.3" ]; then
echo "[FAILED] PHP >= 5.3 required"
fi
Beispiel: Prüfung auf einen leeren String - beispielsweise Skript verlassen, wenn kein Parameter mitgegeben wurde:
if [ -z "$1" ]; then
echo "Usage: $0 parameter"
exit 1
fi
Beispiel: Prüfung, ob Variable nicht leer:
if [ -n "$VAR" ]; then
echo "Value of VAR: $VAR"
fi
Bemerkung
Unbedingt auf das Leerzeichen um den Gleichheitsparameter achten, da das Gleichheitszeichen sonst als Zuweisung interpretiert wird.
Falsch:
myvar="value2"
if [ $myvar=="value1" ]
then
echo "myvar is equal to value1."
else
echo "myvar is equal to value2."
fi
Richtig:
myvar="value2"
if [ $myvar == "value1" ]
then
echo "myvar is equal to value1."
else
echo "myvar is equal to value2."
fi
Numerische Werte
Für binäre oder numerische Vergleiche folgende Operatoren verwenden:
Gleichheit:
-eq
Ungleichheit:
-ne
Grösser als:
-gt
Kleiner als:
-lt
Grösser oder gleich:
-ge
Kleiner oder gleich:
-le
Beispiel:
if [ $MYNUMBER -eq 0 ]; then
...
fi
Numerische Werte nie auf Stringbasis mittels "$MYNUMBER" > 'Wert'
vergleichen - die Shell sortiert alphanumerisch anders, als man im ersten Augenblick des Programmierens erwartet: „12“ ist alphanumerisch immer kleiner als „6“.
Schleifen
for
for i in {1..4}; do
...
done
for i in 18 21 24 27; do ...; done
for i in $(seq 1 4); do
...
done
end=$(date +"%Y")
for year in $(seq 2015 $end)
do
...
done
for i in "$myvar"
do
...
done
Eine for-Schleife lässt sich mit break
beenden.
for file in /tmp*
do
# avoid the loop variable expand to the (un-matching) glob pattern string itself
# if no files are found/match the wildcard:
[ -e "$filename" ] || continue
...
cnt=$(( $cnt + 1 ))
if [ $cnt -gt 1000 ]
then
break
fi
done
For-Schleife mit Counter:
for (( i = 0 ; i < cnt ; i++ ))
do
...
done
For-Schleife über die Values eines Arrays:
for value in "${array[@]}"
do
echo "$value"
done
For-Schleife über ein Array, mit Key und Value:
for key in "${!array[@]}"
do
echo "$key ${array[key]}"
done
Ein paar parktische Beispiele:
# rename a bunch of *.png files to 1.png, 2.png etc. at once
i=1; for file in *.png; do [ -e "$filename" ] || continue; mv $file $i.png; i=$((i+1)); done
# ping all hosts in a subnet
for ip in {1..254}; do ping -q -c 1 172.16.7.$ip; done
# ping all hosts and just show hosts that are down
for ip in {1..254}; do ping -q -c 1 172.16.7.$ip > /dev/null 2> /dev/null; [[ $? != 0 ]] && echo "172.16.7.$ip"; done
Tipp
Das funktioniert nicht, wenn keine Dateien im Verzeichnis vorhanden sind:
for file in $(ls); do echo $file; done
for file in *.log; do echo $file; done
Besser, wenn man eine for-Schleife verwenden möchte:
for file in $(find -type f -iname '*cfg'); do echo $file; done
Auf einen anderen Prozess warten:
while pgrep --euid root process_name > /dev/null; do sleep 1; done
Oder mit until
- angenommen, Maschine ist generell nicht pingbar, und möchte nach einem Reboot automatisch per SSH eingeloggt werden:
ssh linuxfabrik@server.example.com # unsuccessful
until !!; do sleep 5; done # "!!" repeats the last command
Mathematik, Berechnungen
Mit Hilfe von $(( ))
lassen sich einfache Ganzzahl-Berechnungen in Bash durchführen:
echo $((2 + 7))
echo $((2 * 7))
echo $((7 / 2))
# modulo
x=$((23 % 9))
echo $x
Arbeiten mit Zufallswerten:
echo $RANDOM
# random values between 1 and 10
echo $((1 + RANDOM % 10))
# sleep for 1..60 seconds, randomly
sleep $(( $RANDOM % 60 ))
# write random values to a disk
dd if=/dev/urandom of=/dev/sdc
# slower, but better random values
dd if=/dev/random of=/dev/sdc
Sub-Shells, Unterprogramme
Im englischen „Command Substition“ genannt. An einem sehr eleganten Beispiel: die Datei to-delete.list
möge eine Liste zu löschender Dateien enthalten.
rm -f $(/path/to/cat to-delete.list)
Unterkommandos werden immer in einer eigenen, neuen Sub-Shell ausgeführt, die nichts von der aufrufenden Shell weiss. Möchte man Variablen an eine Sub-Shell durchreichen, kommt export
zum Einsatz. Die Sub-Shell darf sie lesen und für sich ändern, die aufrufende Shell bekommt davon jedoch nichts mit.
export myfile=to-delete.list
rm -f $(cat $myfile)
Um in einem Bash-Script andere Bash-Scripte aufzurufen, gibt es mindestens drei Möglichkeiten:
Das andere Skript ausführbar machen und den Shebang
#!/usr/bin/env bash
in die erste Zeile setzen. Nun entweder per$(/path/to/script)
aufrufen, oder den Pfad zum Skript der Umgebungsvariablen$PATH
hinzufügen, damit es als normales Kommando aufgerufen werden kann.Aufrufen per
source /path/to/script
Aufrufen per
/usr/bin/env bash /path/to/script
Die erste und dritte Methode führt das Skript als Sub-Prozess aus, d.h. Funktionen und Variablen aus dem aufrufenden Skript sind nicht verfügbar.
Die zweite Methode führt das Skript innerhalb des aufrufenden Skriptes aus; damit sind Variablen und Funktionen aus dem aufrufenden Skript im Sub-Skript nutzbar.
Wird bei Nutzung der zweiten Methode im Sub-Skript exit
verwendet, wird auch das aurufende Skript beendet, was bei der ersten und dritten Methode nicht passiert.
Debugging
Skript mit Debug-Flag ausführen:
bash -x ./myscript.sh
Teile in einem Skript mit Debug-Flag ausführen:
#!/usr/bin/env bash
# turns on debugging
set -x
...
# turns off debugging
set +x
...
Color-Codes
Wer mit Farben auf der Shell arbeiten möchte, sollte sich auf 8/16 Farben beschränken.
Vordergrund-Farben:
# |
Farbe |
Bash-Code |
---|---|---|
39 |
Default foreground color |
|
30 |
Black |
|
31 |
Red |
|
32 |
Green |
|
33 |
Yellow |
|
34 |
Blue |
|
35 |
Magenta |
|
36 |
Cyan |
|
37 |
Light gray |
|
90 |
Dark gray |
|
91 |
Light red |
|
92 |
Light green |
|
93 |
Light yellow |
|
94 |
Light blue |
|
95 |
Light magenta |
|
96 |
Light cyan |
|
97 |
White |
|
Hintergrund-Farben:
# |
Farbe |
Bash-Code |
---|---|---|
49 |
Default background color |
|
40 |
Black |
|
41 |
Red |
|
42 |
Green |
|
43 |
Yellow |
|
44 |
Blue |
|
45 |
Magenta |
|
46 |
Cyan |
|
47 |
Light gray |
|
100 |
Dark gray |
|
101 |
Light red |
|
102 |
Light green |
|
103 |
Light yellow |
|
104 |
Light blue |
|
105 |
Light magenta |
|
106 |
Light cyan |
|
107 |
White |
|
In Kombination:
echo -e "This is \e[32mGREEN\e[39m, the rest is normal."
Snippets
Test, ob das Skript von „root“ ausgeführt wird:
# Make sure only root can run our script
if [ "$(id -u)" != "0" ]; then
echo "You have to be root." 1>&2
exit 1
fi
# ... your script here ...
Test auf die Existenz eines Benutzers (im Beispiel auf einen PostgreSQL-Benutzer):
# Try to detect the postgres user
if id pgsql >/dev/null 2>&1; then
MYUSER=pgsql
elif id postgres >/dev/null 2>&1; then
MYUSER=postgres
else
exit 0
fi
(CSV-)Datei (Trenner „;“) zeilenweise abarbeiten:
while IFS=\; read -r COL1 COL2 COL3 COL4
do
# ... your script here ...
done < myfile.csv
Prüfen, ob das eigene Skript bereits läuft und so verhindern, dass es mehrfach ausgeführt wird:
# http://mywiki.wooledge.org/BashFAQ/045
if mkdir -- /var/lock/subsys/"$(basename "$0")"; then
trap 'rm -rf -- /var/lock/subsys/"$(basename "$0")"' EXIT # remove directory when script finishes
trap 'rm -rf -- /var/lock/subsys/"$(basename "$0")"' TERM # remove directory when script gets terminated
echo $$ > /var/lock/subsys/"$(basename "$0")"/pid
logger "$0 successfully acquired lock."
else
logger "$0 already running, exiting..."
exit 1
fi
# ... your script here ...
Auf etwas maximal n Sekunden warten, z.B. auf ein „tun0“-Interface:
device="tun0"
t=60
while [[ $t -gt 0 ]]; do
if [[ $(ip link show $device 2> /dev/null) ]]; then
break
else
((t-=1));
sleep 1;
fi
done
Test auf RHEL/CentOS 6 oder 7:
if grep -q -i "(release 6|release 2012)" /etc/redhat-release; then
echo "running RHEL/CentOS 6.x"
elif grep -q -i "(release 7|release 2014)" /etc/redhat-release; then
echo "running RHEL/CentOS 7.x"
fi
Prüfen, ob ein Programm installiert ist (im Beispiel auf needs-restarting
):
command -v needs-restarting >/dev/null 2>&1 || { echo >&2 '"needs-restarting" not found.'; exit 1; }
Built on 2024-11-18