Alias-Scoping in Bash-Funktionen

9

Ich verwende ein Skript (auf das ich keinen Schreibzugriff habe), das eine Reihe von Aliasen erstellt, um eine Umgebung einzurichten. Ich möchte eine Bash-Funktion erstellen, um meine Umgebung einzurichten, aber es scheint, dass die Aliase für den Funktionskörper nicht überleben.

Hier ist ein minimales Beispiel:

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

.

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

.

Wenn ich jetzt einfach aliases.shinteraktiv beschaffe, funktionieren die Dinge wie erwartet:

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

Wenn ich jedoch stattdessen die in meiner .bashrc definierte Funktion aufrufe, erkennt sie den Alias ​​nach der Beschaffung ihrer Definition nicht:

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

Was geht hier vor sich? Fehlt mir etwas am Umfang des aliasBefehls, wenn er in einer Funktion verwendet wird?

Bearbeiten: Ich werde einige Details hinzufügen, die über das minimale Beispiel hinausgehen, um etwas Licht auf das zu werfen, was ich erreichen möchte.

Für meine Arbeit entwickle und starte ich viel Software in einem Cluster und / oder Grid. Ich habe mehrere Projekte, die völlig unterschiedliche Umgebungen erfordern, wie z. B. unterschiedliche gcc-Versionen, bestimmte Softwareversionen, Konfigurations- und Datenpfade sowie verschiedene Umgebungsvariablen. Administratoren stellen die Skripte zum Einrichten verschiedener Dinge bereit, normalerweise durch Definieren von Shell-Funktionen oder Aliasnamen, die andere Funktionen oder Aliase aufrufen oder verschiedene Skripts ausführen. Für mich ist es eine Black Box.

Ich möchte meine eigenen verschiedenen Umgebungen mit einem einzigen Befehl einrichten. Derzeit mache ich so etwas wie:

[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

Diese Befehle müssen im Allgemeinen der Reihe nach ausgeführt werden. Meine Idee war im Grunde, nur die obigen Zeilen in einen Funktionsblock zu kopieren, aber wie das ursprüngliche Beispiel zeigt, funktioniert das einfach nicht. Alternative Problemumgehungen sind mehr als willkommen!

Verfolgungsjagd
quelle

Antworten:

10

Eine alternative Lösung besteht darin, diese Befehle anstelle eines Funktionsblocks in eine Textdatei einzufügen. Etwas wie:

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

Speichern Sie das, setup1.shwo immer Sie möchten. Der Trick ist, dann bezieht diese Datei nicht ausführen:

$ source setup1.sh

Dadurch werden die im Skript enthaltenen Aliase ausgeführt und auch Ihrer aktuellen Shell zur Verfügung gestellt.

Sie können den Vorgang weiter vereinfachen, indem Sie Folgendes zu Ihrem hinzufügen .bashrc:

alias setupLotsOfThings="source setup1.sh"

Jetzt können Sie einfach ausführen setupLotsOfThingsund das gewünschte Verhalten von der Funktion abrufen.


Erläuterung

Hier gibt es zwei Probleme. Erstens sind Aliase für die Funktion, in der sie deklariert sind, nicht verfügbar, sondern erst, wenn diese Funktion beendet wurde, und zweitens, dass Aliase in Skripten nicht verfügbar sind. Beide werden im selben Abschnitt von erklärt man bash:

Aliase werden nicht erweitert, wenn die Shell nicht interaktiv ist, es sei denn, die Shell-Option expand_aliases wird mit shopt festgelegt (siehe Beschreibung von shopt unter SHELL BUILTIN-BEFEHLE unten).

Die Regeln für die Definition und Verwendung von Aliasen sind etwas verwirrend. Bash liest immer mindestens eine vollständige Eingabezeile, bevor einer der Befehle in dieser Zeile ausgeführt wird. Aliase werden erweitert, wenn ein Befehl gelesen wird, nicht wenn er ausgeführt wird. Daher wird eine Aliasdefinition, die in derselben Zeile wie ein anderer Befehl angezeigt wird, erst wirksam, wenn die nächste Eingabezeile gelesen wird. Die Befehle, die der Aliasdefinition in dieser Zeile folgen, sind vom neuen Alias ​​nicht betroffen. Dieses Verhalten ist auch ein Problem, wenn Funktionen ausgeführt werden. Aliase werden beim Lesen einer Funktionsdefinition erweitert, nicht beim Ausführen der Funktion, da eine Funktionsdefinition selbst ein zusammengesetzter Befehl ist. Infolgedessen sind in einer Funktion definierte Aliase erst
verfügbar, nachdem diese Funktion ausgeführt wurde.
Fügen Sie aus Sicherheitsgründen Aliasdefinitionen immer in eine separate Zeile ein und verwenden Sie keinen Alias ​​in zusammengesetzten Befehlen.

Dann gibt es den Unterschied zwischen dem Ausführen und dem Beschaffen einer Datei. Wenn Sie ein Skript ausführen, wird es in einer separaten Shell ausgeführt, während es beim Sourcing in der aktuellen Shell ausgeführt wird. Durch die Beschaffung werden setup.shdie Aliase für die übergeordnete Shell verfügbar, während sie wie ein Skript ausgeführt wird.

Terdon
quelle
Nun, ich arbeite an einem Cluster und habe mehrere solcher "Aliasing" -Skripte zur Verfügung, die beim Einrichten von Softwareumgebungen usw. helfen. Das Alias-Skript definiert viele verschiedene Aliase. Mein Ziel ist es, eine bestimmte Umgebung aufzurufen, indem ich die richtigen Aliase beschaffe und dann (einige) dieser Aliase ausführe. Ich mache das lieber in einem einzigen Befehl, als mehrere Befehle nacheinander ausführen zu müssen.
Verfolgungsjagd
Und PS, das Skript, das die Aliase einrichtet, ist leider sehr ausführlich und etwas langsam (da es viele Dateien auf dem NFS berührt), daher bevorzuge ich es, all diese Dinge nicht zu beschaffen, z. B. beim Anmelden.
Verfolgungsjagd
@chase, aber sie werden nur beim Ausführen bezogen setupLotsOfThings. Sie stehen der Funktion selbst einfach nicht zur Verfügung. Sie funktionieren in der Shell, von der aus Sie die Funktion aufgerufen haben. Wie auch immer, wenn Ihre Funktion nur Aliase enthält, warum nicht einfach einen Alias ​​verwenden? Zum Beispiel : alias setupstuff="source aliases.sh".
Terdon
richtig, aber ich mache mir keine Sorgen um den Umfang an sich . Idealerweise möchte ich nur den Schritt "Quellmaterial" und "Aliase ausführen" in einem kombinieren. Vielleicht kann es mit 3 Funktionen gemacht werden? Funktion sourceStuff () {source ...}; Funktion runStuff () {someAlias; ...}; Funktion setupLotsOfThings () {sourceStuff; runStuff; };
Jagd
@chase ja, daran habe ich gedacht, aber es hat nicht funktioniert :). Könnten Sie Ihre Frage um etwas mehr erweitern, was Sie tun müssen? Funktionen können nur mit so viel Komplexität umgehen, dass Sie möglicherweise stattdessen ein kleines Skript schreiben müssen.
Terdon
7

Tatsächlich sind Ihre Aliase verfügbar, nachdem die Funktion geladen wurde! Sie können sie in Ihrer interaktiven Shell oder .bashrcnach dem Ausführen der Funktion verwenden.

Die Einschränkung besteht darin, dass Aliase in einer Funktionsdefinition beim Lesen der Funktionsdefinition erweitert werden, nicht beim Auswerten der Funktion. Dies ist eine Einschränkung von Bash. Das wird also funktionieren:

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

Aber nicht das:

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

Wenn Sie Aliase benötigen, die innerhalb von Funktionen verwendet werden können und nach dem Analysieren der Funktion definiert werden können, machen Sie sie stattdessen zu Funktionen. Denken Sie daran, dass Sie mit der commandintegrierten Funktion einen externen Befehl von einer gleichnamigen Funktion aus aufrufen können.

Gilles 'SO - hör auf böse zu sein'
quelle