Étendue des alias dans les fonctions bash

9

J'utilise un script (auquel je n'ai pas accès en écriture) qui crée un tas d'alias pour configurer un environnement. Je voudrais créer une fonction bash pour configurer mon environnement, mais il semble que les alias ne survivent pas au corps de la fonction.

Voici un exemple minimal:

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

.

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

.

Maintenant, si je source simplement de manière aliases.shinteractive, les choses fonctionnent comme prévu:

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

Cependant, si j'appelle à la place la fonction définie dans mon .bashrc, il ne reconnaît pas l'alias après avoir obtenu sa définition:

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

Qu'est-ce qui se passe ici? Y a-t-il quelque chose qui me manque dans la portée de la aliascommande lorsqu'elle est utilisée dans une fonction?

Edit: je vais ajouter quelques détails au-delà de l'exemple minimal pour mettre en lumière ce que j'essaie d'accomplir.

Pour mon travail, je développe et exécute de nombreux logiciels sur un cluster et / ou une grille. J'ai plusieurs projets qui nécessitent des environnements complètement différents, tels que différentes versions de gcc, des versions de logiciels spécifiques, des chemins de configuration et de données et diverses variables d'environnement. Les administrateurs fournissent les scripts pour configurer diverses choses, généralement en définissant des fonctions shell ou des alias, qui invoquent d'autres fonctions ou alias ou exécutent divers scripts. Pour moi, c'est une boîte noire.

Je voudrais configurer mes propres environnements avec une seule commande. Actuellement, je fais quelque chose comme:

[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

Ces commandes doivent généralement être exécutées dans l'ordre. Mon idée était simplement de copier les lignes ci-dessus dans un bloc fonction, mais comme le montre l'exemple original, cela ne fonctionne tout simplement pas. Des solutions alternatives sont plus que bienvenues!

chasse
la source

Réponses:

10

Une autre solution consiste à coller ces commandes dans un fichier texte au lieu d'un bloc fonction. Quelque chose comme:

## 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

Enregistrez cela comme setup1.shvous le souhaitez. L'astuce consiste alors à source ce fichier, pas à l'exécuter:

$ source setup1.sh

Cela exécutera les alias qui se trouvent dans le script et les rendra également disponibles pour votre shell actuel.

Vous pouvez encore simplifier le processus en ajoutant ceci à votre .bashrc:

alias setupLotsOfThings="source setup1.sh"

Maintenant, vous pouvez simplement exécuter setupLotsOfThingset obtenir le comportement que vous vouliez de la fonction.


Explication

Il y a deux problèmes ici. Premièrement, les alias ne sont pas disponibles pour la fonction dans laquelle ils sont déclarés, mais seulement une fois que cette fonction est terminée et deuxièmement que les alias ne sont pas disponibles dans les scripts. Les deux sont expliqués dans la même section de man bash:

Les alias ne sont pas développés lorsque le shell n'est pas interactif, sauf si l'option de shell expand_aliases est définie à l'aide de shopt (voir la description de shopt sous SHELL BUILTIN COMMANDS ci-dessous).

Les règles concernant la définition et l'utilisation des alias sont quelque peu confuses. Bash lit toujours au moins une ligne d'entrée complète avant d'exécuter l'une des commandes de cette ligne. Les alias sont développés lors de la lecture d'une commande et non lors de son exécution. Par conséquent, une définition d'alias apparaissant sur la même ligne qu'une autre commande ne prend effet que lorsque la ligne d'entrée suivante est lue. Les commandes suivant la définition d'alias sur cette ligne ne sont pas affectées par le nouvel alias. Ce comportement est également un problème lorsque les fonctions sont exécutées. Les alias sont développés lors de la lecture d'une définition de fonction, pas lors de l'exécution de la fonction, car une définition de fonction est elle-même une commande composée. Par conséquent, les alias définis dans une fonction ne sont
disponibles qu'après l'exécution de cette fonction.
Pour être sûr, placez toujours les définitions d'alias sur une ligne distincte et n'utilisez pas d'alias dans les commandes composées.

Ensuite, il y a la différence entre exécuter et sourcer un fichier. Fondamentalement, l'exécution d'un script le fait fonctionner dans un shell séparé tandis que son sourcing le fait tourner dans le shell courant. Ainsi, l'approvisionnement setup.shmet les alias à la disposition du shell parent tout en l'exécutant comme un script ne le ferait pas.

terdon
la source
Eh bien, je travaille sur un cluster et j'ai plusieurs de ces scripts "d'alias" disponibles qui aident à configurer les environnements logiciels, etc. Le script d'alias définit de nombreux alias différents; mon objectif est d'invoquer un environnement spécifique en recherchant les bons alias, puis en exécutant (certains) ces alias. Je préfère le faire en une seule commande, plutôt que d'exécuter plusieurs commandes en séquence.
poursuivre le
Et PS, le script qui configure les alias est malheureusement très bavard et un peu lent (car il touche de nombreux fichiers sur le NFS), donc je préfère ne pas me procurer toutes ces choses, par exemple au moment de la connexion.
poursuivre le
@chase mais ils ne proviennent que lorsque vous exécutez setupLotsOfThings, ils ne sont tout simplement pas disponibles pour la fonction elle-même. Ils fonctionnent depuis le shell dont vous avez appelé la fonction. Quoi qu'il en soit, si votre fonction ne source que des alias, pourquoi ne pas simplement utiliser un alias? Par exemple: alias setupstuff="source aliases.sh".
terdon
à droite, mais je ne suis pas préoccupé par la portée en soi . Idéalement, je veux simplement combiner le "truc source" et l'étape "exécuter les alias" en un. Peut-être que cela peut être fait avec 3 fonctions? function sourceStuff () {source ...}; function runStuff () {someAlias; ...}; fonction setupLotsOfThings () {sourceStuff; runStuff; };
poursuivre le
@chase ouais, j'y ai pensé mais ça n'a pas marché :). Pourriez-vous développer votre question avec un peu plus de ce que vous devez faire? Les fonctions ne peuvent gérer qu'une telle complexité, vous devrez peut-être écrire un petit script à la place.
terdon
7

En fait, vos alias sont disponibles après le chargement de la fonction! Vous pouvez les utiliser dans votre shell interactif, ou dans votre .bashrcaprès avoir exécuté la fonction.

La restriction est que les alias dans une définition de fonction sont développés lorsque la définition de fonction est lue, et non lorsque la fonction est évaluée. Il s'agit d'une limitation de bash. Cela fonctionnera donc:

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

Mais pas ça:

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

Si vous avez besoin d'alias utilisables à l'intérieur des fonctions et pouvant être définis après l'analyse de la fonction, faites-en plutôt des fonctions. N'oubliez pas que vous pouvez utiliser la fonction commandintégrée pour appeler une commande externe à partir d'une fonction du même nom.

Gilles 'SO- arrête d'être méchant'
la source