Alias ​​scoping nelle funzioni bash


9

Uso uno script (al quale non ho accesso in scrittura) che crea un gruppo di alias per impostare un ambiente. Vorrei creare una funzione bash per configurare il mio ambiente, ma sembra che gli alias non sopravvivano al corpo della funzione.

Ecco un esempio minimo:

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

.

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

.

Ora, se semplicemente fonte aliases.shinterattivamente, le cose funzionano come previsto:

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

Tuttavia, se invece chiamo la funzione definita nel mio .bashrc, non riconosce l'alias dopo aver acquisito la sua definizione:

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

Cosa sta succedendo qui? C'è qualcosa che mi manca nell'ambito del aliascomando quando utilizzato in una funzione?

Modifica: aggiungerò alcuni dettagli oltre l'esempio minimo per far luce su ciò che sto cercando di realizzare.

Per il mio lavoro sviluppo e eseguo molti software su un cluster e / o griglia. Ho diversi progetti che richiedono ambienti completamente diversi, come diverse versioni di gcc, versioni software specifiche, percorsi di configurazione e dati e varie variabili di ambiente. Gli amministratori forniscono gli script per impostare varie cose, di solito definendo funzioni o alias di shell, che invocano altre funzioni o alias o eseguono vari script. Per me è una scatola nera.

Vorrei configurare i miei vari ambienti con un solo comando. Attualmente, faccio qualcosa del tipo:

[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

Questi comandi generalmente devono essere eseguiti in ordine. La mia idea era fondamentalmente di copiare le righe sopra in un blocco funzione, ma come mostra l'esempio originale, semplicemente non funziona. Soluzioni alternative sono più che benvenute!

Risposte:


10

Una soluzione alternativa è quella di incollare quei comandi in un file di testo anziché in un blocco funzione. Qualcosa di simile a:

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

Salvalo come setup1.shpreferisci. Il trucco è quindi procurarsi questo file, non eseguirlo:

$ source setup1.sh

Ciò eseguirà gli alias presenti nello script e li renderà disponibili per la shell corrente.

Puoi semplificare ulteriormente il processo aggiungendo questo al tuo .bashrc:

alias setupLotsOfThings="source setup1.sh"

Ora puoi semplicemente eseguire setupLotsOfThingse ottenere il comportamento desiderato dalla funzione.


Spiegazione

Ci sono due problemi qui. Innanzitutto, gli alias non sono disponibili per la funzione in cui sono dichiarati, ma solo una volta che tale funzione è uscita e in secondo luogo che gli alias non sono disponibili negli script. Entrambi sono spiegati nella stessa sezione di man bash:

Gli alias non vengono espansi quando la shell non è interattiva, a meno che l'opzione di shell expand_aliases sia impostata utilizzando shopt (vedere la descrizione di shopt sotto COMANDI INCORPORATI SHELL di seguito).

Le regole relative alla definizione e all'uso degli alias sono alquanto confuse. Bash legge sempre almeno una riga completa di input prima di eseguire uno qualsiasi dei comandi su quella riga. Gli alias vengono espansi quando viene letto un comando, non quando viene eseguito. Pertanto, una definizione di alias che appare sulla stessa riga di un altro comando non ha effetto fino a quando non viene letta la riga successiva di input. I comandi che seguono la definizione di alias su quella riga non sono interessati dal nuovo alias. Questo comportamento è anche un problema quando vengono eseguite le funzioni. Gli alias vengono espansi quando viene letta una definizione di funzione, non quando viene eseguita la funzione, poiché una definizione di funzione è essa stessa un comando composto. Di conseguenza, gli alias definiti in una funzione non sono
disponibili fino a quando non viene eseguita quella funzione.
Per sicurezza, posiziona sempre le definizioni di alias su una riga separata e non utilizzare l'alias nei comandi composti.

Quindi, c'è la differenza tra l'esecuzione e l'approvvigionamento di un file. Fondamentalmente, l'esecuzione di uno script lo fa funzionare in una shell separata mentre l'approvvigionamento lo fa funzionare nella shell corrente. Quindi, il sourcing setup.shrende gli alias disponibili per la shell genitore mentre lo esegue come uno script no.


Bene, sto lavorando su un cluster e ho a disposizione molti di questi script di "aliasing" che aiutano a configurare ambienti software, ecc. Lo script di alias definisce molti diversi alias; il mio obiettivo è invocare un ambiente specifico acquistando gli alias corretti e quindi eseguendo (alcuni di) quegli alias. Preferirei farlo in un solo comando, piuttosto che dover eseguire diversi comandi in sequenza.
insegui il

E purtroppo lo script che imposta gli alias è purtroppo molto dettagliato e un po 'lento (in quanto tocca molti file su NFS), quindi preferisco non procurarmi tutte queste cose, ad esempio al momento del login.
insegui il

@chase ma provengono solo quando esegui setupLotsOfThings, non sono disponibili per la funzione stessa. Funzionano sulla shell da cui hai chiamato la funzione. Ad ogni modo, se la tua funzione genera solo alias perché non usare un alias? Ad esempio: alias setupstuff="source aliases.sh".
terdon,

giusto, ma non sono preoccupato per l'ambito di per sé . Idealmente, voglio solo combinare il "materiale sorgente" e il passo "esegui alias" in uno. Forse può essere fatto con 3 funzioni? funzione sourceStuff () {source ...}; funzione runStuff () {someAlias; ...}; funzione setupLotsOfThings () {sourceStuff; runStuff; };
insegui il

@chase sì, ci ho pensato ma non ha funzionato :). Potresti espandere la tua domanda con qualcosa in più di ciò che devi fare? Le funzioni possono gestire solo tanta complessità, potresti invece dover scrivere un piccolo script.
terdon,

7

In realtà, i tuoi alias sono disponibili dopo il caricamento della funzione! Puoi usarli nella tua shell interattiva o nel tuo .bashrcdopo aver eseguito la funzione.

La limitazione è che gli alias in una definizione di funzione vengono espansi quando viene letta la definizione di funzione, non quando viene valutata la funzione. Questa è una limitazione di bash. Quindi questo funzionerà:

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

Ma non questo:

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

Se hai bisogno di alias utilizzabili all'interno di funzioni e che possono essere definiti dopo l'analisi della funzione, rendili invece funzioni. Ricorda che puoi usare il commandbuiltin per invocare un comando esterno da una funzione con lo stesso nome.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.