Manchmal müssen Sie mehrzeilige Dokumente mit komplexen verschachtelten Strukturen wie YAML oder HTML aus Bash-Skripten heraus generieren. Sie können dies erreichen, indem Sie einige spezielle Bash-Funktionen verwenden, wie hier Dokumente . Ein „Hier-Dokument“ ist ein Code- oder Textblock, der zu einem Skript oder interaktiven Programm umgeleitet werden kann. Im Wesentlichen wird ein Bash-Skript zu einem Here-Dokument, wenn es zu einem anderen Befehl, Skript oder interaktiven Programm weiterleitet.
In diesem Artikel wird Folgendes erläutert:
- Verwenden Sie Arrays, Wörterbücher und Zähler
- Mit verschiedenen Arten von Kommentaren arbeiten
- Generieren Sie YAML- und HTML-Dokumente
- E-Mails mit Text und Anhängen senden
[ Jetzt herunterladen:Eine Anleitung für Systemadministratoren zum Bash-Scripting. ]
Ein Skript dokumentieren
Es ist wichtig, Ihre Skripte zu kommentieren, und Sie können einzeilige Kommentare mit einem #
erstellen , oder Sie können mehrzeilige Kommentare erstellen, indem Sie die Kombination aus :
verwenden und <<ANYTAG
.
Zum Beispiel:
# This is a simple comment
: <<COMMENT
This is a multi-line comment
Very useful for some complex comments
COMMENT
Diese Hilfefunktion für Ihr Skript ist ein weiteres nützliches Beispiel:
#!/bin/bash
SCRIPT=$(/usr/bin/basename $0)|| exit 100
export SCRIPT
function help_me {
/usr/bin/cat<<EOF
$SCRIPT -- A cool script that names and oh wait...
------------------------------------------------------
$SCRIPT --arg1 \$VALUE --arg2 \$VALUE2
EOF
help_me
}
# To use the help function just call help
help_me
Das mehrzeilige Format ist an sich schon ziemlich nützlich, besonders beim Dokumentieren komplexer Skripte. Es gibt jedoch eine nette Wendung bei der Verwendung dieser Dokumente, die Sie vielleicht schon einmal gesehen haben:
$ /usr/bin/cat<<EOF>$HOME/test_doc.txt
Here is a multi-line document that I want to save.
Note how I can use variables inside like HOME=$HOME.
EOF
Folgendes steht in der Datei:
$ /usr/bin/cat $HOME/test_doc.txt
Here is a multi-line document that I want to save.
Note how I can use variables inside like HOME=/home/josevnz.
Jetzt werde ich zu etwas anderem übergehen, damit Sie dieses Wissen anwenden können.
[Für weitere Bash-Tipps laden Sie dieses Bash Shell Scripting Cheat Sheet herunter]
Verwenden von Arrays und Wörterbüchern zum Generieren einer Ansible-Inventar-YAML-Datei
Angenommen, Sie haben die folgende CSV-Datei mit einer Liste von Hosts in jeder Zeile, die Server oder Desktops enthält:
# List of hosts, tagged by group
macmini2:servers
raspberrypi:servers
dmaf5:desktops
mac-pro-1-1:desktops
Sie möchten die Liste in die folgende Ansible-YAML-Inventardatei konvertieren:
---
all:
children:
servers:
hosts:
macmini2:
raspberrypi:
vars:
description: Linux servers for the Nunez family
desktops:
hosts:
dmaf5:
mac-pro-1-1:
vars:
description: Desktops for the Nunez family
Zusätzliche Einschränkungen:
- Jeder Systemtyp (Desktops oder Server) hat eine andere Variable namens
description
. Durch die Verwendung von Arrays und assoziativen Arrays und Zählern können Sie diese Anforderung erfüllen. - Das Skript sollte fehlschlagen, wenn der Benutzer nicht alle richtigen Tags bereitstellt. Eine unvollständige Bestandsaufnahme ist nicht akzeptabel. Für diese Anforderung hilft ein einfacher Zähler.
Dieses Skript erfüllt das Ziel:
#!/bin/bash
:<<DOC
Convert a file in the following format to Ansible YAML:
# List of hosts, tagged by group
macmini2:servers
raspberrypi:servers
dmaf5:desktops
mac-pro-1-1:desktops
DOC
SCRIPT="$(/usr/bin/basename "$0")"|| exit 100
function help {
/usr/bin/cat<<EOF
Example:
$SCRIPT $HOME/inventory_file.csv servers desktops
EOF
}
# We could use a complicated if-then-else or a case ... esac
# to handle the tag description logic
# with an Associate Array is very simple
declare -A var_by_tag
var_by_tag["desktops"]="Desktops for the Nunez family"
var_by_tag["servers"]="Linux servers for the Nunez family"
function extract_hosts {
tag=$1
host_file=$2
/usr/bin/grep -P ":$tag$" "$host_file"| /usr/bin/cut -f1 -d':'
test $? -eq 0 && return 0|| return 1
}
# Consume the host file
hosts_file=$1
shift 1
if [ -z "$hosts_file" ]; then
echo "ERROR: Missing host file!"
help
exit 100
fi
if [ ! -f "$hosts_file" ]; then
echo "ERROR: Cannot use provided host file: $hosts_file"
help
exit 100
fi
# Consume the tags
if [ -z "$*" ]; then
echo "ERROR: You need to provide one or more tags for the script to work!"
help
exit 100
fi
: <<DOC
Generate the YAML
The most annoying part is to make sure the indentation is correct. YAML depends entirely on proper indentation.
The idea is to iterate through the tags and perform the proper actions based on each.
DOC
for tag in "$@"; do # Quick check for tag description handling. Show the user available tags if that happens
if [ -z "${var_by_tag[$tag]}" ]; then
echo "ERROR: I don't know how to handle tag=$tag (known tags=${!var_by_tag[*]}). Fix the script!"
exit 100
fi
done
/usr/bin/cat<<YAML
---
all:
children:
YAML
# I do want to split by spaces to initialize my array, this is OK:
# shellcheck disable=SC2207
for tag in "$@"; do
/usr/bin/cat<<YAML
$tag:
hosts:
YAML
declare -a hosts=($(extract_hosts "$tag" "$hosts_file"))|| exit 100
host_cnt=0 # Declare your counter
for host in "${hosts[@]}"; do
/usr/bin/cat<<YAML
$host:
YAML
((host_cnt+=1)) # This is how you increment a counter
done
if [ "$host_cnt" -lt 1 ]; then
echo "ERROR: Could not find a single host with tag=$tag"
exit 100
fi
/usr/bin/cat<<YAML
vars:
description: ${var_by_tag[$tag]}
YAML
done
So sieht die Ausgabe aus:
all:
children:
servers:
hosts:
macmini2:
raspberrypi:
vars:
description: Linux servers for the Nunez family
desktops:
hosts:
dmaf5:
mac-pro-1-1:
vars:
description: Desktops for the Nunez family
Ein besserer Weg könnte darin bestehen, ein dynamisches Inventar zu erstellen und es vom Ansible-Playbook verwenden zu lassen. Um das Beispiel einfach zu halten, habe ich das hier nicht gemacht.
Versenden von HTML-E-Mails mit YAML-Anhängen
Das letzte Beispiel zeigt Ihnen, wie Sie ein Here-Dokument an Mozilla Thunderbird weiterleiten (Sie können etwas Ähnliches mit /usr/bin/mailx
machen ), um eine Nachricht mit einem HTML-Dokument und Anhängen zu erstellen:
#!/bin/bash
:<<HELP
Please take a look a the following document so you understand the Thunderbird command line below:
http://kb.mozillazine.org/Command_line_arguments_-_Thunderbird
HELP
declare EMAIL
EMAIL=$1
test -n "$EMAIL"|| exit 100
declare ATTACHMENT
test -n "$2"|| exit 100
test -f "$2"|| exit 100
ATTACHMENT="$(/usr/bin/realpath "$2")"|| exit 100
declare DATE
declare TIME
declare USER
declare KERNEL_VERSION
DATE=$(/usr/bin/date '+%Y%m%d')|| exit 100
TIME=$(/usr/bin/date '+%H:%M:%s')|| exit 100
USER=$(/usr/bin/id --real --user --name)|| exit 100
KERNEL_VERSION=$(/usr/bin/uname -a)|| exit 100
/usr/bin/cat<<EMAIL| /usr/bin/thunderbird -compose "to='$EMAIL',subject='Example of here documents with Bash',message='/dev/stdin',attachment='$ATTACHMENT'"
<!DOCTYPE html>
<html>
<head>
<style>
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
</style>
</head>
<body>
<h2>Hello,</p> <b>This is a public announcement from $USER:</h2>
<table>
<tr>
<th>Date</th>
<th>Time</th>
<th>Kernel version</th>
</tr>
<tr>
<td>$DATE</td>
<td>$TIME Rovelli</td>
<td>$KERNEL_VERSION</td>
</tr>
</table>
</body>
</html>
EMAIL
Dann können Sie das Mailer-Skript aufrufen:
$ ./html_mail.sh [email protected] hosts.yaml
Wenn alles wie erwartet läuft, erstellt Thunderbird eine E-Mail wie diese:
Abschluss
Zusammenfassend haben Sie Folgendes gelernt:
- Verwenden Sie komplexere Datenstrukturen wie Arrays und assoziative Arrays, um Dokumente zu generieren
- Verwenden Sie Zähler, um Ereignisse zu verfolgen
- Verwenden Sie diese Dokumente, um YAML-Dokumente, Hilfeanweisungen, HTML usw. zu erstellen.
- E-Mails mit HTML und YAML versenden
Bash ist in Ordnung, um kleine, unkomplizierte Dokumente zu erstellen. Wenn Sie mit großen oder komplexen Dokumenten arbeiten, ist es möglicherweise besser, eine andere Skriptsprache wie Python oder Perl zu verwenden, um die gleichen Ergebnisse mit weniger Aufwand zu erzielen. Unterschätzen Sie außerdem niemals die Bedeutung eines echten Debuggers, wenn Sie sich mit der Erstellung komplexer Dokumente befassen.