Comprobación de comandos necesarios en Bash y la cuestión de las interfaces
Hola, soy un incompetente.
Habiendo terminado mi último día de trabajo, recibí chocolates de una chica del mismo lugar de trabajo, me llevaron a comer yakiniku justo antes de dejar el trabajo, y pasé un buen rato hablando con una persona que había sido un especialista en redes durante 25 años sobre su entorno de red doméstico pasado y actual, y yo sobre mi propio entorno de servidor doméstico.
Aunque creo que logré hacer lo mejor que pude con mis limitadas habilidades, siento pena porque soy una persona con muchas deficiencias.
Siento a diario que los scripts de shell son extremadamente potentes.
Por supuesto, su baja portabilidad es un hecho, ya que no pueden escribir funciones como csh, el shell predeterminado en FreeBSD, y ese problema es innegable.
Sin embargo, creo que la verdadera fortaleza de los scripts de shell reside en la facilidad con la que manejan las interfaces.
Interfaces de C y scripts de shell
Por ejemplo, supongamos que se usa echo en un script de shell de la siguiente manera.
echo "test"
Esto corresponde a la salida estándar en el lenguaje C.
#include <stdio.h>
int main() {
const char *message = "test";
// Imprime el mensaje en la salida estándar
fprintf(stdout, "%s\n", message);
return 0;
}
Si se desea imprimir en la salida de error estándar, se realiza una redirección de la siguiente manera.
echo "testerr" 1>&2
Y en el caso del lenguaje C, corresponde a lo siguiente.
#include <stdio.h>
int main() {
const char *message = "testerr";
// Imprime el mensaje en la salida de error estándar
fprintf(stderr, "%s\n", message);
return 0;
}
Además, los códigos de retorno de la mayoría de los comandos de GNU/Linux, en caso de fallo, están ingeniosamente diseñados gracias a los pioneros del pasado, lo que facilita la prueba de las condiciones de decisión y es bastante efectivo si se maneja como un lenguaje de scripting.
Sin embargo, el problema es que esto solo se beneficia en entornos donde se pueden usar scripts de shell.
Aunque se escriba un código similar en otros lenguajes, su versatilidad es efectiva, pero hay partes donde los comandos operan como lo que en un lenguaje se llamarían bibliotecas, y siento que ese es el motivo por el que se dice que carece de versatilidad.
Sin embargo, la capacidad de manipular descriptores de archivo de una manera tan simplificada me parece extremadamente potente.
Para el else, solo se conectan dos pipes.
※Pensándolo después de escribirlo, estrictamente hablando, no es un else ^^;
Simplemente actúa como un else cuando el valor de retorno no es 0.
$ curl aa || echo "wtf"
curl: (6) Could not resolve host: aa
wtf
¡Qué bueno hubiera sido si hubiera sabido de algo tan útil cuando era estudiante de secundaria!
Ojalá cualquier sitio web hubiera ofrecido más oportunidades para aprender scripts de shell.
De hecho, ahora que lo recuerdo, el entorno japonés de GNU/Linux en aquel entonces era bastante difícil, y recuerdo que la conversión predictiva de entrada japonesa no era práctica; así que la facilidad actual podría deberse a la proliferación de Chrome OS, que ha permitido el florecimiento de la entrada japonesa OSS.
También recuerdo haber configurado un ThinkPad con Ubuntu para un amigo excéntrico que lo quería cuando yo era estudiante de secundaria...
¿Entonces, cómo se comprueban los comandos?
Me preguntaba si habría una forma más inteligente y legible de escribir la comprobación de comandos en un script de shell de Bash.
Dicho esto, aunque es conveniente pasar los comandos a un bucle for desde un array, que ya es una forma inteligente, veamos la sintaxis para almacenar en un array.
#!/bin/bash
cmds=(aa bb cc)
for i in ${cmds[@]}; do
command -v $i 1> /dev/null || echo "Please install $i"
done
Se almacena en cmds como un array usando (), y se expande el array con ${cmds[@]}.
Sin embargo, si se añaden comandos a cmds=() a medida que aumentan, uno empieza a preguntarse si realmente es necesario escribirlos aquí.
En otros lenguajes, la información de las dependencias se encuentra en archivos como Cargo.toml, deno.json, go.mod, Gemfile, o el a menudo criticado requirements.txt.
Sería conveniente tener un archivo que agrupe todo de esa manera.
Así que lo hice de esta manera.
#!/bin/bash
cat ./cmd | xargs which 1> /dev/null || echo "Please install cmd"
Y, ¿cómo se ve el archivo cmd?
$ cat cmd
ls
pwd
cargo
En realidad, quería comprobarlo con el comando command, pero command funciona como un comando interno del shell y no pude pasarlo a xargs.
$ which command
which: no command in (/home/haturatu/.rbenv/bin:/home/haturatu/.rbenv/shims:/home/haturatu/.deno/bin:/home/haturatu/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/opt/android-sdk/platform-tools:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/lib/plan9/bin:/home/haturatu/.local/bin:/home/haturatu/.local/bin:/home/haturatu/Android/Sdk/cmdline-tools/tools/bin:/home/haturatu/Android/Sdk/platform-tools:/home/haturatu/Android/Sdk/emulator:/home/haturatu/Android/Sdk/tools:/home/haturatu/Android/Sdk/tools/bin:/home/haturatu/Android/Sdk/cmdline-tools/tools/bin:/home/haturatu/Android/Sdk/platform-tools:/home/haturatu/Android/Sdk/emulator:/home/haturatu/Android/Sdk/tools:/home/haturatu/Android/Sdk/tools/bin:/home/haturatu/.npm-global/bin:/home/haturatu/go/bin)
Pido disculpas si me equivoco, pero creo que cuando se pasa a xargs, se ejecuta como un proceso de xargs separado del proceso de bash que lo ejecutó, por lo que no puede ser reconocido.
Por lo tanto, no se puede pasar el comando que falló con which a Please install cmd, pero pensé que no era necesario ya que la salida de error de which aparece antes de eso.
※Pensándolo después de escribirlo, ¿quizás funcionaría con xargs /bin/bash command -v...?
Hay otra ventaja de tener este grupo de comandos en un archivo separado.
Se pueden instalar todos juntos desde este archivo.
cat cmd | xargs -I {} sudo pacman -S --noconfirm {}
En el caso de apt, se puede hacer de manera similar con la opción -y.
Así que, dejando de lado si realmente lo usaré o no, lo dejo como una idea.
Hasta la próxima. Saludos. (Es solo una ilusión que haya escrito varios artículos desde el artículo de cierre de fin de año)