Alcance de alias en funciones bash

9

Utilizo un script (al que no tengo acceso de escritura) que crea un grupo de alias para configurar un entorno. Me gustaría crear una función bash para configurar mi entorno, pero parece que los alias no sobreviven al cuerpo de la función.

Aquí hay un ejemplo mínimo:

# aliases.sh
alias fooAlias='echo "this will never work!"'  

.

# .bashrc
function setupLotsOfThings() {
    source aliases.sh
    fooAlias
}

.

Ahora, si simplemente origen aliases.shinteractivamente, las cosas funcionan como se esperaba:

[mycomputer]~/ $ source aliases.sh
[mycomputer]~/ $ fooAlias
this will never work!

Sin embargo, si en su lugar llamo a la función definida en mi .bashrc, no reconoce el alias después de obtener su definición:

[mycomputer]~/ $ setupLotsOfThings
-bash: fooAlias: command not found

¿Que esta pasando aqui? ¿Hay algo que me falta sobre el alcance del aliascomando cuando se usa en una función?

Editar: agregaré algunos detalles más allá del ejemplo mínimo para arrojar algo de luz sobre lo que estoy tratando de lograr.

Para mi trabajo, desarrollo y ejecuto una gran cantidad de software en un clúster y / o grilla. Tengo varios proyectos que requieren entornos completamente diferentes, como diferentes versiones de gcc, lanzamientos de software específicos, RUTA de configuración y datos, y varias variables de entorno. Los administradores proporcionan los scripts para configurar varias cosas, generalmente definiendo funciones o alias de shell, que invocan otras funciones o alias o ejecutan varios scripts. Para mí, es una caja negra.

Me gustaría configurar mis propios entornos con un solo comando. Actualmente, hago algo como:

[mycomputer]~/ $ source /some/environment/setup/script.sh
[mycomputer]~/ $ aliasToSetupSomeSoftwareVersion    #this was defined in the above
[mycomputer]~/ $ anotherAliasForOtherSoftware
[mycomputer]~/ $ source /maybe/theres/another/script.sh
[mycomputer]~/ $ runSomeOtherSetup      # this was defined in the new script

Estos comandos generalmente tienen que ejecutarse en orden. Básicamente, mi idea era copiar las líneas anteriores en un bloque de funciones, pero como muestra el ejemplo original, eso simplemente no funciona. ¡Las soluciones alternativas son más que bienvenidas!

persecución
fuente

Respuestas:

10

Una solución alternativa es pegar esos comandos en un archivo de texto en lugar de un bloque de funciones. Algo como:

## This is needed to make the sourced aliases available
## within the script.
shopt -s expand_aliases

source /some/environment/setup/script.sh
aliasToSetupSomeSoftwareVersion
anotherAliasForOtherSoftware
source /maybe/theres/another/script.sh
runSomeOtherSetup

Guarda eso como setup1.shquieras El truco consiste en obtener este archivo, no ejecutarlo:

$ source setup1.sh

Eso ejecutará los alias que están en el script y también los pondrá a disposición de su shell actual.

Puede simplificar aún más el proceso agregando esto a su .bashrc:

alias setupLotsOfThings="source setup1.sh"

Ahora puede simplemente ejecutar setupLotsOfThingsy obtener el comportamiento que desea de la función.


Explicación

Hay dos problemas aquí. Primero, los alias no están disponibles para la función en la que están declarados, pero solo una vez que esa función ha salido y segundo, que los alias no están disponibles dentro de los scripts. Ambos se explican en la misma sección de man bash:

Los alias no se expanden cuando el shell no es interactivo, a menos que la opción del shell expand_aliases se configure usando shopt (consulte la descripción de shopt en SHELL BUILTIN COMMANDS a continuación).

Las reglas relativas a la definición y uso de alias son algo confusas. Bash siempre lee al menos una línea completa de entrada antes de ejecutar cualquiera de los comandos en esa línea. Los alias se expanden cuando se lee un comando, no cuando se ejecuta. Por lo tanto, una definición de alias que aparece en la misma línea que otro comando no tiene efecto hasta que se lea la siguiente línea de entrada. Los comandos que siguen a la definición de alias en esa línea no se ven afectados por el nuevo alias. Este comportamiento también es un problema cuando se ejecutan funciones. Los alias se expanden cuando se lee una definición de función, no cuando se ejecuta la función, porque una definición de función es en sí misma un comando compuesto. Como consecuencia, los alias definidos en una función no están
disponibles hasta después de que se ejecuta esa función.
Para estar seguro, siempre coloque las definiciones de alias en una línea separada y no use alias en los comandos compuestos.

Luego, existe la diferencia entre ejecutar y obtener un archivo. Básicamente, ejecutar un script hace que se ejecute en un shell separado, mientras que el abastecimiento lo hace ejecutarse en el shell actual. Por lo tanto, el abastecimiento setup.shhace que los alias estén disponibles para el shell principal mientras se ejecuta como un script no lo haría.

terdon
fuente
Bueno, estoy trabajando en un clúster y tengo varios scripts de "alias" disponibles que ayudan a configurar entornos de software, etc. El script de alias define muchos alias diferentes; mi objetivo es invocar un entorno específico obteniendo los alias correctos y luego ejecutando (algunos de) esos alias. Prefiero hacer esto en un solo comando, que tener que ejecutar varios comandos en secuencia.
persecución
Y, por desgracia, el script que configura los alias es muy detallado y un poco lento (ya que toca muchos archivos en el NFS), por lo que prefiero no obtener todas estas cosas, por ejemplo, al momento de iniciar sesión.
persecución
@chase pero solo se obtienen cuando se ejecuta setupLotsOfThings, simplemente no están disponibles para la función en sí. Funcionan desde el shell desde el que llamó a la función. De todos modos, si su función solo genera alias, ¿por qué no usar un alias? Por ejemplo: alias setupstuff="source aliases.sh".
terdon
correcto, pero no estoy preocupado por el alcance per se . Idealmente, solo quiero combinar el "material fuente" y el paso "ejecutar alias" en uno. ¿Quizás se puede hacer con 3 funciones? función sourceStuff () {fuente ...}; función runStuff () {someAlias; ...}; función setupLotsOfThings () {sourceStuff; runStuff; };
perseguir el
@ Compre, sí, pensé en eso, pero no funcionó :). ¿Podría ampliar su pregunta con algo más de lo que necesita hacer? Las funciones solo pueden manejar tanta complejidad, es posible que tengas que escribir un pequeño guión en su lugar.
terdon
7

En realidad, ¡tus alias están disponibles después de cargar la función! Puede usarlos en su shell interactivo o en su .bashrcdespués de ejecutar la función.

La restricción es que los alias en una definición de función se expanden cuando se lee la definición de función, no cuando se evalúa la función. Esta es una limitación de bash. Entonces esto funcionará:

function setupLotsOfThings() {
    source aliases.sh
}
setupLotsOfThings
fooAlias

Pero no esto:

function setupLotsOfThings() {
    source aliases.sh
}
function useTheAliases() {
    fooAlias
}
setupLotsOfThings
useTheAliases

Si necesita alias que se puedan utilizar dentro de las funciones y puedan definirse después de analizar la función, conviértalas en funciones. Recuerde que puede usar la función commandincorporada para invocar un comando externo desde una función del mismo nombre.

Gilles 'SO- deja de ser malvado'
fuente