En Bash, ¿cuándo usar un alias, cuándo escribir un script y cuándo escribir una función?

360

Me tomó casi 10 años de uso de Linux hacer esta pregunta. Todo fue prueba y error y navegación aleatoria por Internet a altas horas de la noche.

Pero la gente no debería necesitar 10 años para esto. Si recién comenzara con Linux, me gustaría saber: ¿ Cuándo usar un alias, cuándo escribir un script y cuándo escribir una función?

En lo que respecta a los alias, uso alias para operaciones muy simples que no toman argumentos.

alias houston='cd /home/username/.scripts/'

Eso parece obvio. Pero algunas personas hacen esto:

alias command="bash bashscriptname"

(y agregarlo al .bashrcarchivo)

¿Hay una buena razón para hacer eso? Estoy intentando mucho, pero realmente no puedo pensar en ninguna circunstancia en la que me gustaría hacer eso. Entonces, si hay un caso límite en el que eso marcaría la diferencia, responda a continuación.

Porque ahí es donde simplemente pondría algo en mi RUTA y chmod +xeso, que es otra cosa que surgió después de años de prueba y error de Linux.

Lo que me lleva al siguiente tema. Por ejemplo, agregué una carpeta oculta ( .scripts/) en el directorio de inicio a mi RUTA simplemente agregando una línea a mi .bashrc( PATH=$PATH:/home/username/.scripts/), por lo que todo lo ejecutable allí se autocompleta automáticamente.

Si lo necesitara.

Sin embargo, realmente no necesito eso, ¿verdad? Solo lo usaría para lenguajes que no son el shell, como Python.

Si es el shell, puedo escribir una función dentro del mismo .bashrc:

funcname () {
  somecommand -someARGS "$@"
}

Como dije, descubrí mucho de esto a través de prueba y error. Y realmente solo vi la belleza de las funciones cuando mi computadora murió y me vi obligado a usar las computadoras de las personas que me rodeaban cuando no las usaban.

En lugar de mover un directorio completo de scripts de computadora a computadora, terminé reemplazando el .bashrc de todos los demás por el mío, ya que nunca habían hecho una sola modificación.

¿Pero extrañé algo?

Entonces, ¿qué le diría a un usuario principiante de Linux sobre cuándo usar alias, cuándo escribir un script y cuándo escribir una función?

Si no es obvio, supongo que las personas que respondan esto harán uso de las tres opciones. Si solo usa alias, o solo usa scripts, o solo usa funciones, o si solo usa alias y scripts o alias y funciones o scripts y funciones, esta pregunta no está realmente dirigida a usted.

ixtmixilix
fuente
55
posible duplicado de funciones bash vs scripts
Gilles
10
+1 para indicar explícitamente todos los subconjuntos de {alias, script, function} a los que esta pregunta no apunta. +1 para la fe infantil de que estaba bien que omitieras el subconjunto nulo.
Thomas L Holaday
1
Aunque la pregunta específicamente se refería a bash, tenga en cuenta que los shells Bourne anteriores tenían 'alias' pero no funciones. Esto podría marcar la diferencia si le preocupa la compatibilidad.
AndyB
Si .bashrc realmente es el mejor lugar, o al menos un lugar sólido, para esto? Hay tantas maneras de hacer lo mismo en Linux, que agradezco, sin embargo, en igualdad de condiciones, prefiero hacer las cosas de la manera más común.
Kit10

Respuestas:

242

Un alias no debería (en general) hacer más que cambiar las opciones predeterminadas de un comando. No es más que un simple reemplazo de texto en el nombre del comando. No puede hacer nada con argumentos, sino pasarlos al comando que realmente ejecuta. Entonces, si simplemente necesita agregar un argumento al frente de un solo comando, un alias funcionará. Ejemplos comunes son

# Make ls output in color by default.
alias ls="ls --color=auto"
# make mv ask before overwriting a file by default
alias mv="mv -i"

Se debe usar una función cuando necesita hacer algo más complejo que un alias, pero eso no sería útil por sí solo. Por ejemplo, tome esta respuesta en una pregunta que hice sobre cómo cambiar grepel comportamiento predeterminado de acuerdo con si está en proceso:

grep() { 
    if [[ -t 1 ]]; then 
        command grep -n "$@"
    else 
        command grep "$@"
    fi
}

Es un ejemplo perfecto de una función porque es demasiado compleja para un alias (que requiere valores predeterminados diferentes según una condición), pero no es algo que necesitará en un script no interactivo.

Si obtiene demasiadas funciones o funciones demasiado grandes, colóquelas en archivos separados en un directorio oculto y consúltelas en su ~/.bashrc:

if [ -d ~/.bash_functions ]; then
    for file in ~/.bash_functions/*; do
        . "$file"
    done
fi

Un guión debe sostenerse por sí mismo. Debe tener valor como algo que pueda reutilizarse o utilizarse para más de un propósito.

Kevin
fuente
14
También es importante recordar que, a menos que se obtenga con .o source, un script se ejecuta mediante un proceso bash separado y tiene su propio entorno. Por esta razón, cualquier cosa que modifique el entorno de shell (por ejemplo, funciones, variables, etc.) no persistirá en el entorno de shell desde el que ejecuta el script.
Will Vousden
260

Las otras respuestas proporcionan algunas pautas generales suaves basadas en el gusto personal, pero ignoran muchos hechos pertinentes que uno debe considerar al decidir entre guiones, funciones o alias.

Alias ​​y funciones ¹

  • Todo el contenido de los alias y las funciones se almacena en la memoria del shell.
  • Una consecuencia natural de esto son los alias y las funciones solo pueden ser utilizadas por el shell actual, y no por ningún otro programa que pueda invocar desde el shell, como editores de texto, scripts o incluso instancias secundarias del mismo shell.
  • Los alias y las funciones son ejecutados por el shell actual, es decir, se ejecutan dentro y afectan el entorno actual del shell .² No es necesario ningún proceso separado para ejecutar un alias o función.

Guiones

  • Los shells no guardan los scripts en la memoria. En cambio, los scripts se leen de los archivos donde se almacenan cada vez que se necesitan. Si el script se encuentra a través de una $PATHbúsqueda, muchos shells almacenan un hash de su nombre de ruta en la memoria para ahorrar tiempo en futuras $PATHbúsquedas, pero ese es el alcance de la huella de memoria de un script cuando no está en uso.
  • Las secuencias de comandos se pueden invocar de más formas que las funciones y los alias. Se pueden pasar como argumento a un intérprete, como sh script, o invocarse directamente como un ejecutable, en cuyo caso #!/bin/shse invoca al intérprete en la línea shebang (por ejemplo ) para ejecutarlo. En ambos casos, el script es ejecutado por un proceso de intérprete separado con su propio entorno separado del de su shell, cuyo entorno el script no puede afectar de ninguna manera. De hecho, el intérprete de shell ni siquiera tiene que coincidir con el shell de invocación. Debido a que los scripts invocados de esta manera parecen comportarse como cualquier ejecutable ordinario, cualquier programa puede usarlos.

    Por último, un guión puede ser leído y ejecutado por el shell actual con ., o en algunos proyectiles, source. En este caso, el script se comporta como una función que se lee a pedido en lugar de mantenerse constantemente en la memoria.

Solicitud

Dado lo anterior, podemos llegar a algunas pautas generales sobre si hacer algo un script o una función / alias.

  • ¿Es necesario que otros programas, además de su shell, puedan usarlo? Si es así, tiene que ser un guión.

  • ¿Solo desea que esté disponible desde un shell interactivo? Es común querer cambiar el comportamiento predeterminado de muchos comandos cuando se ejecutan interactivamente sin afectar los comandos / scripts externos. Para este caso, use un conjunto de alias / funciones en el archivo rc "modo interactivo solamente" del shell (para bashesto es .bashrc).

  • ¿Necesita cambiar el entorno del shell? Tanto una función / alias como un script de origen son posibles opciones.

  • ¿Es algo que usas con frecuencia? Probablemente sea más eficiente mantenerlo en la memoria, así que conviértalo en una función / alias si es posible.

  • Por el contrario, ¿es algo que usas raramente? En ese caso, no tiene sentido tener memoria de memoria cuando no la necesita, así que conviértalo en un script.


¹ Si bien las funciones y los alias tienen algunas diferencias importantes, se agrupan porque las funciones pueden hacer todo lo que pueden hacer los alias. Los alias no pueden tener variables locales ni pueden procesar argumentos, y son inconvenientes para algo más que una línea.

² Cada proceso en ejecución en un sistema Unix tiene un entorno que consiste en un grupo de variable=valuepares que a menudo contienen configuraciones de configuración global, como LANGla configuración regional predeterminada y PATHpara especificar la ruta de búsqueda ejecutable.

jw013
fuente
26
En mi humilde opinión Esta es la mejor respuesta.
Luc M
44
El formato de pregunta / respuesta es una gran idea. Podría robar eso. ;-)
Mikel
44
Vale la pena señalar: si dos (o más) scripts necesitan compartir algún código, probablemente sea mejor poner ese código en una función que se encuentra en un tercer archivo que ambos scripts importan / fuente.
kbolino
3
Otro elemento para agregar a esa lista de preguntas: ¿Alguna vez necesita cambiar la funcionalidad en el comando sobre la marcha? los cambios en un script se reflejarán en todas las sesiones, mientras que las funciones y los alias se deben volver a cargar o redefinir por sesión.
Stratus3D
3
Buena respuesta. Una cosa más importante (para mí): al crear un "acceso directo" a otras utilidades, es mejor usar un alias porque el autocompletado existente solo funcionará con alias, pero no con scripts o funciones (por lo tanto, +1 para alias). Por ejemplo, al crear un alias g='gradle'autocompletado de Gradle cuando uso mi galias, pero no lo saco de fábrica cuando uso un script gradle $*o una función congradle $@
Yoav Aharoni
37

Creo que depende del gusto de cada persona. Para mí la lógica es así:

  • Primero trato de hacer un alias, porque es el más simple.
  • Si la cosa es demasiado complicada para caber en una línea, trato de que sea una función.
  • Cuando la función comienza a crecer más allá de una docena de líneas, la pongo en un script.

Realmente no hay nada que te impida hacer algo que funcione .

phunehehe
fuente
66
A menudo omito la opción de función y hago un script de inmediato. Pero estoy de acuerdo en que es en parte una cuestión de gustos
Bernhard
2
Una función comienza a tener sentido si la necesita en varios scripts.
Nils
77
... o si necesita los efectos secundarios para alterar el shell actual .
Glenn Jackman
tiene sentido, hombre!
explorador
15

Al menos parcialmente es una cuestión de gusto personal. Por otro lado, hay algunas distinciones funcionales claras:

  • alias: solo adecuado para reemplazos de texto simples, sin argumentos / parámetros
  • funciones: fácil de escribir / usar, capacidad de script de shell completo, solo disponible dentro de bash
  • scripts: más o menos como funciones, pero también disponibles (invocables) fuera de bash

En cuanto a las secuencias de comandos de shell que he hecho en los últimos años, he dejado de escribir alias (porque todas tienden a convertirse en funciones con el tiempo) y hago secuencias de comandos solo si necesitan estar disponibles también en entornos que no sean bash.

PD: En alias command="bash bashscriptname"realidad, no veo ninguna razón para hacer esto. Incluso si bashscriptnameno está en $ PATH, un simple alias c=/path/to/scriptsería suficiente.

nohillside
fuente
1
En alias command="bash bashscriptname"el script no necesariamente tiene que ser ejecutable; en el alias c=/path/to/scripttiene que hacerlo.
Martin - マ ー チ ン
No es cierto en absoluto que las funciones "solo estén disponibles dentro de Bash". Si quiere decir que son una característica de solo Bash, eso es simplemente falso (Bourne shell y todas las derivadas compatibles las tienen); y si quiere decir que son una característica de los shells interactivos, eso tampoco es preciso (aunque los alias, las variables y las funciones definidas en un archivo que se carga al inicio mediante shells interactivos obviamente no se cargarán con shells no interactivos).
tripleee
@tripleee El significado era más parecido a "no puedes exec()
usar
11

Aquí hay algunos puntos adicionales sobre alias y funciones:

  • El alias y la función del mismo nombre pueden coexistir
  • el espacio de nombres de alias se busca primero (ver primer ejemplo)
  • los alias no se pueden (des) establecer en subcapas o entornos no interactivos (ver segundo ejemplo)

Por ejemplo:

alias f='echo Alias'; f             # prints "Alias"
function f { echo 'Function'; }; f  # prints "Alias"
unalias f; f                        # prints "Function"

Como podemos ver, hay espacios de nombres separados para alias y funciones; se pueden encontrar más detalles usando declare -A -p BASH_ALIASESy declare -f f, que imprime sus definiciones (ambos se almacenan en la memoria).

Ejemplo que muestra limitaciones de alias:

alias a='echo Alias'
a        # OK: prints "Alias"
eval a;  # OK: prints "Alias"
( alias a="Nested"; a );  # prints "Alias" (not "Nested")
( unalias a; a );         # prints "Alias"
bash -c "alias aa='Another Alias'; aa"  # ERROR: bash: aa: command not found

Como podemos ver, los alias no son anidables, a diferencia de las funciones. Además, su uso se limita a las sesiones interactivas.

Finalmente, tenga en cuenta que puede tener un cálculo arbitrario en un alias al declarar una función que la llame de inmediato, así:

alias a_complex_thing='f() { do_stuff_in_function; } f'

que ya se usa ampliamente en el caso de los alias de Git. El beneficio de hacerlo sobre la declaración de una función es que su alias no se puede sobrescribir simplemente al generar (o usar .) un script que declare una función con el mismo nombre.

leden
fuente
10

Cuándo escribir un guión ...

  • Los scripts ensamblan componentes de software (también conocidos como herramientas, comandos, procesos, ejecutables, programas) en componentes más complejos, que pueden ser ensamblados en componentes aún más complejos.
  • Los scripts generalmente se hacen ejecutables para que puedan llamarse por su nombre. Cuando se llama, se genera un nuevo subproceso para que se ejecute el script. Las copias de cualquier exportvariable y / o función ed se pasan por valor al script. Los cambios en esas variables no se propagan de nuevo al script principal.
  • Las secuencias de comandos también se pueden cargar (originar) como si fueran parte de la secuencia de comandos de llamada. Esto es análogo a lo que otros idiomas llaman "importar" o "incluir". Cuando se obtienen, se ejecutan dentro del proceso existente. No se genera ningún subproceso.

Cuándo escribir una función ...

  • Las funciones son efectivamente scripts de shell precargados. Funcionan un poco mejor que llamar a un script separado, pero solo si debe leerse desde un disco mecánico. La proliferación actual de unidades flash, SSD y el almacenamiento en caché normal de Linux en RAM no utilizada hacen que esa mejora sea en gran medida inconmensurable.
  • Las funciones sirven como los principales medios de bash para lograr modularidad, encapsulación y reutilización. Mejoran la claridad, fiabilidad y facilidad de mantenimiento de los scripts.
  • Las reglas de sintaxis para llamar a una función son idénticas a las de llamar a un ejecutable. Se invocaría una función con el mismo nombre que un ejecutable en lugar del ejecutable.
  • Las funciones son locales para el script en el que se encuentran.
  • Las funciones se pueden exportar ( copiar por valor ) para que se puedan usar dentro de los llamados scripts. Por lo tanto, las funciones solo se propagan a procesos secundarios, nunca a los padres.
  • Las funciones crean comandos reutilizables que a menudo se ensamblan en bibliotecas (una secuencia de comandos con solo definiciones de funciones) para que sean generadas por otras secuencias de comandos.

Cuando escribir un alias ...

Dentro de los scripts como los de la biblioteca, a veces se necesita un alias para una función, como cuando se cambia el nombre de una función pero se requiere compatibilidad con versiones anteriores. Esto se puede lograr creando una función simple con el nombre antiguo que pase todos sus argumentos a la nueva función ...

# A bash in-script 'alias'
function oldFunction () { newFunction "$@"; }
DocSalvager
fuente
9

Otra cosa que no creo que se haya mencionado: una función se ejecuta en el contexto del proceso de invocación, mientras que un script bifurca un nuevo shell.

Esto podría ser importante para el rendimiento: una función es más rápida, ya que no lo hace fork()y exec(). En circunstancias normales, la diferencia es trivial, pero si está depurando un sistema que no tiene memoria y está agitando las páginas, podría hacer una gran diferencia.

Además, si desea modificar su entorno actual de shell, debe usar una función. Por ejemplo, una función podría cambiar la búsqueda de comandos $PATHpara el shell actual, pero un script no puede, porque opera en una copia fork / exec de $PATH.

Jan Steinman
fuente
¿Cómo funciona esta propagación de funciones a los niños?
HappyFace
1
@HappyFace En Bash puedes hacer export -funa función, aunque el funcionamiento interno preciso de esto es algo oscuro. Esto no es portátil al shell Bourne tradicional, creo.
tripleee
7

Script y alias y script y función no son mutuamente excluyentes. Puede almacenar y almacenar alias y funciones en scripts.

Los scripts son solo código que se hace persistente . Las funciones útiles y los alias que le gustaría usar en el futuro se almacenan en scripts. Sin embargo, un script suele ser una colección de más de una función.

Como los alias no están parametrizados , son muy limitados; generalmente para definir algunos parámetros predeterminados.

Una función es una unidad de código separada , un concepto bien definido de unas pocas líneas de código que no pueden separarse en partes más pequeñas y útiles; uno que puede ser reutilizado directamente u otro por otras funciones.

usuario desconocido
fuente
5

Si debe ser muy rápido, conviértalo en un alias o una función.

Si debe ser utilizable fuera de su shell preferido, conviértalo en un script. 1

Si toma argumentos, conviértalo en una función o un script.

Si necesita contener caracteres especiales, conviértalo en un alias o una secuencia de comandos. 2

Si necesita trabajar con sudo, conviértalo en un alias o un script. 3

Si desea cambiarlo fácilmente sin cerrar sesión e iniciar sesión, un script es más fácil. 4 4

Notas al pie

1 O conviértalo en un alias, póngalo ~/.envy export ENV="$HOME/.env"configúrelo, pero es complicado hacerlo funcionar de forma portátil.

2 Los nombres de funciones deben ser identificadores, por lo que deben comenzar con una letra y solo pueden contener letras, dígitos y guiones bajos. Por ejemplo, tengo un alias alias +='pushd +1'. No puede ser una función.

3 Y agrega el alias alias sudo='sudo '. Lo mismo ocurre con cualquier otro comando como strace, gdbetc., que tome un comando como primer argumento.

4 Ver también: fpath. Por supuesto, también puede hacer source ~/.bashrco similar, pero esto a menudo tiene otros efectos secundarios.

Mikel
fuente
1
No sabía que podías alias +en bash. Curiosamente, después de probar, descubrí que en bash puedes hacer +un alias pero no una función, como dices, pero zsh es lo contrario: +puede ser una función pero no un alias.
Kevin
En zshtienes que escribir alias -- +='some command here'.
Mikel
De alguna manera, no creo que el alias +sea ​​portátil. Vea la especificación POSIX en nombres de alias
jw013
3
Votación a favor para cubrir el sudouso. Con respecto a la nota al pie 4, almaceno mis alias ~/.bash_aliasesy las definiciones de funciones ~/.bash_functionspara poder leerlas fácilmente source(sin peligro de efectos secundarios).
Anthony Geoghegan el
3

Solo para agregar algunas notas:

  • Solo se puede usar un script separado con sudo (como si necesita editar un archivo del sistema), por ejemplo:
sudo v /etc/rc.conf  #where v runs vim in a new terminal window;
  • Solo los alias o las funciones podrían reemplazar los comandos del sistema con el mismo nombre (suponiendo que agregue su directorio de scripts al final de PATH, lo que creo que es aconsejable por seguridad en caso de creación accidental o malévola de script con nombre idéntico a un comando del sistema), por ejemplo:
alias ls='ls --color=auto'  #enable colored output;
  • Los alias y las funciones requieren menos memoria y tiempo para la ejecución, pero tardan en cargarse (ya que el shell tiene que interpretarlos todos antes de mostrarle el aviso). Tenga esto en cuenta si ejecuta nuevos procesos de shell regularmente, por ejemplo:
# pressing key to open new terminal
# waiting for a few seconds before shell prompt finally appears.

Aparte de eso, podría usar la forma más simple posible, es decir, primero considerar el alias, luego la función, luego el script.

corvinus
fuente
55
Los alias también se pueden usar con sudo. Pero primero lo necesitas alias sudo='sudo '.
Mikel
Si bien es cierto que la ejecución de un script consumirá momentáneamente más memoria durante la ejecución de fork + exec, tener un montón de código cargado en la memoria de la instancia de shell actual hace que consuma más memoria en el futuro, a menudo para almacenar código que solo se usa con bastante poca frecuencia.
tripleee
2

Mi regla de oro es:

  • alias: un comando, sin parámetros
  • funciones - un comando algunos parámetros
  • script: varios comandos, sin parámetros
Michael Durrant
fuente
1

En un entorno multiusuario (o multisysamin), uso scripts para todo, incluso si solo termina siendo un breve contenedor 'exec something ....'.

Claro, es técnicamente más lento / menos eficiente que un alias o función, pero eso casi nunca importa, y siempre que esté en la ruta, un script siempre funciona.

Se puede llamar a su función desde cron, desde algo con un entorno reducido o modificado como sudo o env, o el usuario puede estar usando un shell diferente para usted, todo o lo que es probable que rompa un alias o función.

Si tiene algo sensible al rendimiento, maneje eso como un caso especial, o mejor aún, considere que es un disparador para reescribir en un lenguaje de script más funcional.

Si hablamos de características que solo se usarán en otros scripts, entonces también puede considerar definir un shell estándar y escribir un script de biblioteca de funciones que simplemente pueda ser. I fuente a todos los otros guiones.

T

tjb63
fuente
0

Un ejemplo de una situación en la que lo más probable es que desee utilizar un alias.

Sé que esta es una publicación antigua, pero me gustaría señalar una situación en la que casi tuve que usar una combinación de alias con un script y elegí no usar una función.

Tengo un script ~/.bin/llamado setupque hace lo siguiente: 1

  1. me lleva a cierto directorio.
  2. define algunas variables.
  3. imprime mensajes sobre el estado del directorio. 2

El punto es que si solo corriera setup <project-name>, no habría definido esas variables y no habría llegado al directorio en absoluto. La solución que he encontrado para ser el mejor fue la de añadir esta secuencia de comandos PATHy añadir alias setup=". ~/.bin/setup"a ~/.bashrco lo que sea.

Notas:

  1. Utilicé un script para esta tarea y no una función, no porque sea particularmente largo, sino porque puedo editarlo y no tengo que obtener el archivo después de una edición si quiero actualizar su uso.
  2. Un caso similar se me ocurrió cuando creé un script para recargar todos mis archivos de puntos. 3
  1. El script está disponible en mi repositorio de archivos de puntos en .bin/.
  2. Acerca del script : le doy a este script un argumento que es un nombre para el proyecto que definí en avanzado. Luego, el script sabe llevarme al directorio correcto de acuerdo con un determinado csvarchivo. Las variables que define se toman del archivo MAKE en ese directorio. El guión se ejecuta después ls -ly git statusme muestra lo que está sucediendo allí.
  3. Ese script también está disponible en mi repositorio de archivos de puntos .bin/también.
Doron Behar
fuente
1
Hm, parece que debería ser solo una función, no una combinación de alias-script. (Por cierto, los entornos se escriben "entornos", no "ambientes")
Comodín el
Gracias por comentar sobre el error tipográfico, lo arreglaré en la próxima confirmación. En cuanto a usar una función en lugar de un script, tal vez usaré una función para esta tarea en particular y descartaré estos alias. El punto es que a veces es bastante fácil usar un script y un alias si editas ese script de vez en cuando .
Doron Behar
0

Cuando escribir un guion

Cuando desee ejecutar el comando desde una herramienta que no sea el shell.

Esto incluye vim (para mí): tener filtros escritos y otros programas como scripts, puedo hacer algo como :%!my-filterfiltrar un archivo a través del programa desde mi editor.

Si my-filterfuera una función o un alias, eso no sería posible.

D. Ben Knoble
fuente