Los trucos más tontos de Bash: variables, búsqueda, descriptores de archivos y operaciones remotas

Esta publicación de blog es la segunda de una serie de consejos y trucos útiles para aprovechar al máximo el shell Bash. Dentro parte uno, cubrí la historia, el último argumento, el trabajo con archivos y directorios, la lectura de archivos y las funciones de Bash. En este segmento, cubro variables de shell, búsqueda, descriptores de archivos y operaciones remotas.

Índice

    Usar variables de shell

    Las variables de Bash las establece el shell cuando se las llama. ¿Por qué usaría hostname ¿cuándo puedo usar $HOSTNAME o por qué debería usar whoami ¿Cuándo puedo usar $USER? Las variables bash son muy rápidas y no requieren aplicaciones externas.

    Aquí hay algunas variables de uso frecuente:

    $PATH
    $HOME
    $USER
    $HOSTNAME
    $PS1
    ..
    $PS4

    Utilizar el echo Comando para expandir variables. Por ejemplo, la variable de shell $ PATH se puede ampliar ejecutando:

    $> echo $PATH

    Usa el comando de búsqueda

    el find El comando es probablemente una de las herramientas más utilizadas en el sistema operativo Linux. Es extremadamente útil en shells interactivos. También se utiliza en guiones. Con find Puedo enumerar archivos más antiguos o más nuevos que una fecha específica, eliminarlos en función de esa fecha, cambiar los permisos de archivo o directorio, etc.

    Familiaricémonos con este comando.

    Para listar archivos con más de 30 días, simplemente ejecuto:

    $> find /tmp -type f -mtime +30

    Para eliminar archivos de más de 30 días, ejecute:

    $> find /tmp -type f -mtime +30 -exec rm -rf {} ;

    Donde

    $> find /tmp -type f -mtime +30 -exec rm -rf {} +

    Si bien los comandos anteriores eliminarán archivos de más de 30 días, tal como están escritos, bifurcan el rm comando cada vez que encuentran un archivo. Esta investigación se puede escribir de manera más eficiente usando xargs:

    $> find /tmp -name '*.tmp' -exec printf '%s' {} ; | xargs -0 rm

    Puedo usar find lista sha256sum archivos solo ejecutando:

    $> find . -type f -exec sha256sum {} +

    Y ahora para encontrar y eliminar archivos .jpg duplicados:

    $> find . -type f -name '*.jpg' -exec sha256sum {} + | sort -uk1,1 

    Descriptores de archivos de referencia

    En el shell de Bash, los descriptores de archivo (FD) son importantes para manejar la entrada y salida de comandos. Muchas personas tienen problemas para comprender correctamente los descriptores de archivos. Cada proceso tiene tres descriptores de archivo predeterminados, a saber:

    codificadoSentidoSitioLa descripcion
    0entrada estándar/dev/stdinTeclado, archivo o secuencia
    1salida estándar/dev/salida estándarmonitor, terminal, pantalla
    2Error estándar/dev/stderLos códigos de salida distintos de cero suelen ser > FD2, mostrar

    Ahora que sabe qué hacen los FD de forma predeterminada, veámoslos en acción. Comienzo creando un directorio llamado foo, que contiene file1.

    $> ls foo/ bar/
    ls: cannot access 'bar/': No such file or directory
    foo/:
    file1

    La salida pasa al error estándar (stderr) y también se muestra en la pantalla. Ejecutaré el mismo comando, pero esta vez uso 2> omitir estándar:

    $> ls foo/ bar/ 2>/dev/null
    foo/:
    file1

    Es posible enviar la salida de foo a la salida estándar (stdout) y un archivo simultáneamente, e ignore stderr. Por ejemplo:

    $> { ls foo bar | tee -a ls_out_file ;} 2>/dev/null
    foo:
    file1

    Después:

    $> cat ls_out_file
    foo:
    file1

    El siguiente comando envía stdout a un archivo y stderr a /dev/null para que no aparezca el error en pantalla:

    $> ls foo/ bar/ >to_stdout 2>/dev/null
    $> cat to_stdout
    foo/:
    file1

    El siguiente comando envía stdout y stderr al mismo archivo:

    $> ls foo/ bar/ >mixed_output 2>&1
    $> cat mixed_output
    ls: cannot access 'bar/': No such file or directory
    foo/:
    file1

    Esto es lo que sucedió en el último ejemplo, donde stdout y stderr fueron redirigidos al mismo archivo:

        ls foo/ bar/ >mixed_output 2>&1
                 |          |
                 |          Redirect stderr to where stdout is sent
                 |                                                        
                 stdout is sent to mixed_output

    Otro pequeño truco (> Bash 4.4) para enviar stdout y stderr en el mismo archivo es usar el signo ampersand. Por ejemplo:

    $> ls foo/ bar/ &>mixed_output

    Aquí hay una redirección más compleja:

    exec 3>&1 >write_to_file; echo "Hello World"; exec 1>&3 3>&-

    Esto es lo que sucede:

    • exec 3>&1 Copiar stdout al descriptor de archivo 3
    • > write_to_file Hacer FD 1 para escribir en el archivo
    • echo "Hello World" Saltar al archivo porque FD 1 ahora apunta al archivo
    • exec 1>&3 Copiar FD 3 a 1 (intercambiar)
    • Three> & - Cierra el descriptor de archivo tres (ya no lo necesitamos)

    A menudo es conveniente agrupar los comandos y luego enviar la salida estándar a un solo archivo. Por ejemplo:

    $> { ls non_existing_dir; non_existing_command; echo "Hello world"; } 2> to_stderr
    Hello world

    Como puede ver, solo se imprime "Hola mundo" en la pantalla, pero la salida de los comandos fallidos se escribe en el archivo to_stderr.

    Realizar operaciones de forma remota

    Uso Telnet, netcat, Nmap y otras herramientas para probar si un servicio remoto está activo y si puedo conectarme a él. Estas herramientas son convenientes, pero no están instaladas de forma predeterminada en todos los sistemas.

    Afortunadamente, existe una manera fácil de probar una conexión sin usar herramientas externas. Para ver si un servidor remoto está ejecutando un sitio web, una base de datos, SSH o cualquier otro servicio, ejecute:

    $> timeout 3 bash -c ‘</dev/tcp/remote_server/remote_port’ || echo “Failed to connect”

    Por ejemplo, para ver si servidorA ejecuta el servicio MariaDB:

    $> timeout 3 bash -c ‘</dev/tcp/serverA/3306’ || echo “Failed to connect”

    Si la conexión falla, el mensaje aparece en su pantalla.

    Asumir servidorA está detrás de un cortafuegos/NAT. Quiero ver si el firewall está configurado para permitir una conexión a la base de datos. servidorA, pero aún no he instalado un servidor de base de datos. Para emular un puerto de base de datos (o cualquier otro puerto) puedo usar lo siguiente:

    [serverA ~]# nc -l 3306

    En clienteA, Clase:

    [clientA ~]# timeout 3 bash -c ‘</dev/tcp/serverA/3306’ || echo “Failed”

    Mientras hablo de conexiones remotas, ¿qué pasa con la ejecución de comandos en un servidor remoto a través de SSH? Puedo usar el siguiente comando:

    $> ssh remotehost <<EOF  # Press the Enter key here
    > ls /etc
    EOF

    Este comando se ejecuta ls /etc en el host remoto.

    También puedo ejecutar una secuencia de comandos local en el host remoto sin tener que copiar la secuencia de comandos en el servidor remoto. Una forma es ingresar:

    $> ssh remote_host 'bash -s' < local_script

    Otro ejemplo es pasar variables de entorno localmente al servidor remoto y finalizar la sesión después de la ejecución.

    $> exec ssh remote_host ARG1=FOO ARG2=BAR 'bash -s' <<'EOF'
    > printf %s\n "$ARG1" "$ARG2"
    > EOF
    Password:
    FOO
    BAR
    Connection to remote_host closed.

    Hay muchas otras acciones complejas que puedo realizar en el host remoto.

    Conclusión

    Definitivamente hay más en Bash de lo que pude cubrir en esta publicación de blog de dos partes. Comparto lo que sé y lo que experimento a diario. La idea es familiarizarse con algunas técnicas que pueden hacer que su trabajo sea menos propenso a errores y más divertido.

    Artículos de interés

    Subir