GNU/Linux >> Tutoriales Linux >  >> Linux

Cómo crear documentos con scripts Bash

A veces es necesario generar documentos de varias líneas con estructuras anidadas complejas, como YAML o HTML, desde scripts de Bash. Puede lograr esto usando algunas funciones especiales de Bash, como documentos aquí . Un "documento aquí" es un código o bloque de texto que se puede redirigir a un script o programa interactivo. Esencialmente, una secuencia de comandos de Bash se convierte en un documento aquí cuando se redirige a otro comando, secuencia de comandos o programa interactivo.

Este artículo explica cómo:

  • Usar matrices, diccionarios y contadores
  • Trabajar con diferentes tipos de comentarios
  • Generar documentos YAML y HTML
  • Envíe correos electrónicos con texto y archivos adjuntos

[ Descargar ahora:una guía para administradores de sistemas sobre secuencias de comandos Bash. ]

Documentar un guión

Es importante comentar sus secuencias de comandos y puede crear comentarios de una sola línea con un # , o puede tener comentarios de varias líneas usando la combinación de : y <<ANYTAG .

Por ejemplo:

# This is a simple comment
: <<COMMENT

This is a multi-line comment
Very useful for some complex comments

COMMENT

Esta función de ayuda para su secuencia de comandos es otro ejemplo útil:

#!/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

El formato de varias líneas es bastante útil por sí mismo, especialmente cuando se documentan guiones complejos. Sin embargo, hay un buen giro en el uso de documentos aquí que quizás hayas visto antes:

$ /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

Esto es lo que está escrito en el archivo:

$ /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.

Ahora pasaré a otra cosa para que puedas aplicar este conocimiento.

[ Para obtener más consejos sobre Bash, descargue esta Hoja de trucos de secuencias de comandos de Bash Shell]

Uso de matrices y diccionarios para generar un archivo YAML de inventario de Ansible

Supongamos que tiene el siguiente archivo CSV con una lista de hosts en cada línea que contiene servidores o escritorios:

# List of hosts, tagged by group
macmini2:servers
raspberrypi:servers
dmaf5:desktops
mac-pro-1-1:desktops

Desea convertir la lista al siguiente archivo de inventario YAML de Ansible:

---
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        

Restricciones adicionales:

  • Cada tipo de sistema (escritorios o servidores) tendrá una variable diferente llamada description . El uso de matrices y matrices y contadores asociativos le permite satisfacer este requisito.
  • La secuencia de comandos debería fallar si el usuario no proporciona todas las etiquetas correctas. Un inventario incompleto no es aceptable. Para este requisito, un simple contador ayudará.

Este script logra el objetivo:

#!/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

Así es como se ve la salida:

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

Una mejor manera podría ser crear un inventario dinámico y dejar que el libro de jugadas de Ansible lo use. Para simplificar el ejemplo, no hice eso aquí.

Enviar correos electrónicos HTML con archivos adjuntos YAML

El último ejemplo le mostrará cómo canalizar un documento aquí a Mozilla Thunderbird (puede hacer algo similar con /usr/bin/mailx ) para crear un mensaje con un documento HTML y archivos adjuntos:

#!/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

Luego puede llamar al script de correo:

$ ./html_mail.sh [email protected] hosts.yaml

Si todo sale como se esperaba, Thunderbird creará un correo electrónico como este:

Conclusión

En resumen, ha aprendido a:

  • Utilice estructuras de datos más sofisticadas, como matrices y matrices asociativas, para generar documentos
  • Utilice contadores para realizar un seguimiento de los eventos
  • Utilice aquí documentos para crear documentos YAML, instrucciones de ayuda, HTML, etc.
  • Envíe correos electrónicos con HTML y YAML

Bash está bien para generar documentos pequeños y sin complicaciones. Si se trata de documentos grandes o complejos, es mejor que utilice otro lenguaje de secuencias de comandos como Python o Perl para obtener los mismos resultados con menos esfuerzo. Además, nunca subestime la importancia de un depurador real cuando se trata de la creación de documentos complejos.


Linux
  1. Cómo crear documentos LaTeX con Emacs

  2. Cómo crear una base de datos en MySQL con MySQL Workbench

  3. Cómo crear scripts Bash usando variables externas y scripts incrustados

  4. ¿Cómo crear una máquina virtual desde cero con Virsh?

  5. Cómo crear un pico de CPU con un comando bash

Cómo crear un grupo de volúmenes en Linux con LVM

Cómo crear documentos en Ubuntu

Cómo usar el comando echo en Bash Scripts en Linux

Cómo trabajar con declaraciones de casos en scripts Bash

Cómo crear un comercio electrónico con Magento

Cómo crear arañas web con Scrapy