tutorial script programacion español bash shell built-in

bash - script - programacion en shell



¿Cuál es el propósito de:(colon) GNU Bash builtin? (11)

Funciones de autodocumentación.

También puede utilizar : para incrustar documentación en una función.

Supongamos que tiene un script de biblioteca mylib.sh , que proporciona una variedad de funciones. Puede obtener la biblioteca ( . mylib.sh ) y llamar a las funciones directamente después de eso ( lib_function1 arg1 arg2 ), o evitar saturar su espacio de nombres e invocar la biblioteca con un argumento de función ( mylib.sh lib_function1 arg1 arg2 ).

¿No sería agradable si también pudiera escribir mylib.sh --help y obtener una lista de las funciones disponibles y su uso, sin tener que mantener manualmente la lista de funciones en el texto de ayuda?

#!/bin/bash # all "public" functions must start with this prefix LIB_PREFIX=''lib_'' # "public" library functions lib_function1() { : This function does something complicated with two arguments. : : Parameters: : '' arg1 - first argument ($1)'' : '' arg2 - second argument'' : : Result: : " it''s complicated" # actual function code starts here } lib_function2() { : Function documentation # function code here } # help function --help() { echo MyLib v0.0.1 echo echo Usage: mylib.sh [function_name [args]] echo echo Available functions: declare -f | sed -n -e ''/^''$LIB_PREFIX''/,/^}$/{//(^''$LIB_PREFIX''/)/|/(^[ /t]*:/)/{ s/^/(''$LIB_PREFIX''.*/) ()//n=== /1 ===/;s/^[ /t]*: /?[''/'''"]/?/ /;s/[''/'''"]/?;/?$//;p}}'' } # main code if [ "${BASH_SOURCE[0]}" = "${0}" ]; then # the script was executed instead of sourced # invoke requested function or display help if [ "$(type -t - "$1" 2>/dev/null)" = function ]; then "$@" else --help fi fi

Algunos comentarios sobre el código:

  1. Todas las funciones "públicas" tienen el mismo prefijo. Solo estos deben ser invocados por el usuario y aparecer en el texto de ayuda.
  2. La función de autodocumentación se basa en el punto anterior y utiliza declare -f para enumerar todas las funciones disponibles, luego las filtra para mostrar solo funciones con el prefijo apropiado.
  3. Es una buena idea encerrar la documentación entre comillas simples, para evitar la expansión no deseada y la eliminación de espacios en blanco. También deberá tener cuidado al usar apóstrofes / citas en el texto.
  4. Podría escribir código para internalizar el prefijo de la biblioteca, es decir, el usuario solo tiene que escribir mylib.sh function1 y se traduce internamente a lib_function1 . Este es un ejercicio que le queda al lector.
  5. La función de ayuda se llama "--help". Este es un enfoque conveniente (es decir, perezoso) que utiliza el mecanismo de invocación de la biblioteca para mostrar la ayuda en sí, sin tener que codificar un cheque adicional por $1 . Al mismo tiempo, abarrotará su espacio de nombres si obtiene la biblioteca. Si no le gusta eso, puede cambiar el nombre a algo como lib_help o, en realidad, consultar los argumentos para --help en el código principal e invocar la función de ayuda manualmente.

¿Cuál es el propósito de un comando que no hace nada, siendo poco más que un líder de comentarios, pero en realidad es un shell incorporado en sí mismo?

Es más lento que insertar un comentario en sus scripts en aproximadamente un 40% por llamada, lo que probablemente varía mucho según el tamaño del comentario. Las únicas razones posibles que puedo ver son las siguientes:

# poor man''s delay function for ((x=0;x<100000;++x)) ; do : ; done # inserting comments into string of commands command ; command ; : we need a comment in here for some reason ; command # an alias for `true'' (lazy programming) while : ; do command ; done

Supongo que lo que realmente estoy buscando es qué aplicación histórica podría haber tenido.


Dos usos más no mencionados en otras respuestas:

Explotación florestal

Toma este script de ejemplo:

set -x : Logging message here example_command

La primera línea, set -x , hace que el shell imprima el comando antes de ejecutarlo. Es una construcción bastante útil. El inconveniente es que el tipo de echo Log message habitual de la declaración ahora imprime el mensaje dos veces. El método del colon se acerca a eso. Tenga en cuenta que todavía tendrá que escapar de caracteres especiales como lo haría con echo .

Puestos de trabajo de cron

Lo he visto siendo usado en trabajos cron, como este:

45 10 * * * : Backup for database ; /opt/backup.sh

Este es un trabajo cron que ejecuta el script /opt/backup.sh todos los días a las 10:45. La ventaja de esta técnica es que permite que los temas del correo electrónico /opt/backup.sh mejor apariencia cuando /opt/backup.sh imprime algunos resultados.


Es similar a pass en Python.

Un uso sería apagar una función hasta que se escriba:

future_function () { :; }


Lo uso para habilitar / deshabilitar fácilmente comandos de variables:

#!/bin/bash if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then vecho=":" # no "verbose echo" else vecho=echo # enable "verbose echo" fi $vecho "Verbose echo is ON"

Así

$ ./vecho $ VERBOSE=1 ./vecho Verbose echo is ON

Esto hace que para un script limpio. Esto no se puede hacer con ''#''.

También,

: >afile

es una de las formas más simples de garantizar que exista un ''archivo'', pero tiene una longitud de 0.


Podría usarlo junto con backticks ( `` ) para ejecutar un comando sin mostrar su salida, como esto:

: `some_command`

Por supuesto, solo some_command > /dev/null hacer some_command > /dev/null , pero : -version es algo más corto.

Dicho esto, no recomendaría hacerlo porque confundiría a la gente. Simplemente vino a la mente como un posible caso de uso.


Si desea truncar un archivo a cero bytes, útil para borrar registros, intente esto:

:> file.log


También es útil para programas políglotas:

#!/usr/bin/env sh '':'' //; exec "$(command -v node)" "$0" "$@" ~function(){ ... }

Ahora se trata tanto de un script de shell ejecutable como de un programa JavaScript: lo que significa que ./filename.js , sh filename.js y node filename.js funcionan.

(Definitivamente un poco de un uso extraño, pero no obstante efectivo).

Algunas explicaciones, según lo solicitado:

  • Shell-scripts se evalúan línea por línea; y el comando exec , cuando se ejecuta, termina el shell y reemplaza su proceso con el comando resultante. Esto significa que para el shell, el programa se ve así:

    #!/usr/bin/env sh '':'' //; exec "$(command -v node)" "$0" "$@"

  • Mientras no se produzca una expansión de parámetros o alias en la palabra, cualquier palabra en un shell-script puede incluirse entre comillas sin cambiar su significado; esto significa que '':'' es equivalente a : (solo lo hemos incluido entre comillas para lograr la semántica de JavaScript que se describe a continuación)

  • ... y como se describió anteriormente, el primer comando en la primera línea es un no-op (se traduce a : // , o si prefiere citar las palabras, '':'' ''//'' . Observe que el // no tiene un significado especial aquí, como lo hace en JavaScript; es solo una palabra sin sentido que se está tirando.)

  • Finalmente, el segundo comando en la primera línea (después del punto y coma), es la verdadera fuente del programa: es la llamada exec que reemplaza al shell-script que se invoca , con un proceso Node.js invocado para evaluar el resto del script .

  • Mientras tanto, la primera línea, en JavaScript, se analiza como un literal de cadena ( '':'' ), y luego un comentario, que se elimina; Por lo tanto, para JavaScript, el programa se ve así:

    '':'' ~function(){ ... }

    Dado que la cadena literal está en una línea por sí misma, es una declaración sin opción de operación y, por lo tanto, se elimina del programa; eso significa que se elimina toda la línea, dejando solo su código de programa (en este ejemplo, la function(){ ... } cuerpo).


Una aplicación útil para: es si solo está interesado en usar expansiones de parámetros para sus efectos secundarios en lugar de pasar su resultado a un comando. En ese caso, usa el PE como un argumento para: o falso dependiendo de si desea un estado de salida de 0 o 1. Un ejemplo podría ser : "${var:=$1}" . Dado que : es un componente debe ser bastante rápido.


Vi este uso en un script y pensé que era un buen sustituto para invocar basename dentro de un script.

oldIFS=$IFS IFS=/ for basetool in $0 ; do : ; done IFS=$oldIFS

... este es un reemplazo para el código: basetool=$(basename $0)


: también puede ser para comentarios de bloque (similar a / * * / en lenguaje C). Por ejemplo, si desea omitir un bloque de código en su script, puede hacer esto:

: << ''SKIP'' your code block here SKIP


Históricamente , los shells Bourne no tenían true y false como comandos integrados. true fue en cambio simplemente alias a : y false a algo como let 0 .

: es ligeramente mejor que true para la portabilidad a las antiguas conchas derivadas de Bourne. Como un simple ejemplo, considere no tener ni el ! operador de tubería ni el || Operador de lista (como fue el caso de algunas conchas antiguas de Bourne). Esto deja a la cláusula else de la sentencia if como el único medio para bifurcar en función del estado de salida:

if command; then :; else ...; fi

Dado que if requiere una cláusula no vacía, los comentarios no cuentan como no vacíos, sirve como no-op.

Hoy en día (es decir, en un contexto moderno) usualmente puedes usar : o true . Ambos están especificados por POSIX, y algunos encuentran true más fácil de leer. Sin embargo, hay una diferencia interesante : es un llamado POSIX especial incorporado , mientras que true es un incorporado regular .

  • Se requieren elementos especiales incorporados en el shell; Las incorporaciones normales solo están "típicamente" integradas, pero no están estrictamente garantizadas. Generalmente no debería haber un programa regular llamado : con la función true en PATH de la mayoría de los sistemas.

  • Probablemente la diferencia más importante es que con las incorporaciones especiales, cualquier variable establecida por la incorporada, incluso en el entorno durante la evaluación simple del comando, persiste una vez que se completa el comando, como se demuestra aquí con ksh93:

    $ unset x; ( x=hi :; echo "$x" ) hi $ ( x=hi true; echo "$x" ) $

    Tenga en cuenta que Zsh ignora este requisito, al igual que el GNU Bash, excepto cuando se opera en el modo de compatibilidad POSIX, pero todos los demás shells "derivados de POSIX sh" observan esto, incluyendo dash, ksh93 y mksh.

  • Otra diferencia es que los componentes incorporados regulares deben ser compatibles con exec , como se demuestra aquí con Bash:

    $ ( exec : ) -bash: exec: :: not found $ ( exec true ) $

  • POSIX también señala explícitamente que : puede ser más rápido que true , aunque esto es, por supuesto, un detalle específico de la implementación.