Gibt es eine Möglichkeit, source
ein Shell-Skript in einen Namespace zu verschieben, vorzugsweise ein Bash-Shell-Skript, aber ich würde andere Shells untersuchen, wenn sie diese Funktion hätten und Bash nicht.
Was ich damit meine, ist z. B. "Präfixieren aller definierten Symbole mit etwas, damit sie nicht mit bereits definierten Symbolen (Variablennamen, Funktionsnamen, Aliasnamen) kollidieren" oder eine andere Funktion, die Namenskollisionen verhindert.
Wenn es eine Lösung gibt, bei der ich gleichzeitig einen Namespace source
( NodeJS
Stil) erstellen kann, ist dies die beste.
Beispielcode:
$ echo 'hi(){ echo Hello, world; }' > english.sh
$ echo 'hi(){ echo Ahoj, světe; }' > czech.sh
$ . english.sh
$ hi
#=> Hello, world
$ . czech.sh #bash doesn't even warn me that `hi` is being overwritten here
$ hi
#=> Ahoj, světe
#Can't use the English hi now
#And sourcing the appropriate file before each invocation wouldn't be very efficient
( easiest thing ever )
. Aber das ist nicht ganz das, wonach du suchst. Ich denke, Sie könnten es tun( stuff in subshell; exec env ) | sed 's/^/namespace_/'
undeval
das Ergebnis in der übergeordneten Shell, aber das ist irgendwie böse.ksh93
. Namespaces sind von grundlegender Bedeutung - und alle Namenstypen (die auch typisierbar sind) unterstützen den Namespace. Es ist auch in praktisch jeder Hinsicht viel schneller alsbash
übrigens.env | sed ...
würde für Variablenset
funktionieren , ich könnte tun , um Funktionen zu erhalten, aber das Suchen und Ersetzen wäre ein Problem - Funktionen können sich gegenseitig aufrufen und Sie, so dass Sie alle Kreuzaufrufe durch vorangestellte Kreuzaufrufe ersetzen müssten, ohne jedoch die zu ersetzen gleiche Wörter an anderer Stelle im Funktionsdefinitionscode, wo es sich nicht um einen Aufruf handelt. Dafür benötigen Sie einen Bash-Parser, nicht nur einen regulären Ausdruck, und er funktioniert immer noch nur, solange sich die Funktionen nicht über eval gegenseitig aufrufen.Antworten:
Von
man ksh
auf einem System mit einemksh93
installierten ...Zur Veranschaulichung wird hier das Konzept auf einen Namespace angewendet, der standardmäßig für jede reguläre Shell-Variable bereitgestellt wird, die in einer
ksh93
Shell zugewiesen ist . Im folgenden Beispiel werde ich einediscipline
Funktion definieren , die als zugewiesene.get
Methode für die$PS1
Shell-Variable fungiert. Jedes Shell - Variable wird im Grunde seinen eigenen Namensraum mit zumindest der Standardget
,set
,append
, undunset
Methoden. Nach dem Definieren der folgenden Funktion wird$PS1
die Ausgabe von jedes Mal, wenn auf die Variable in der Shell verwiesendate
wird, am oberen Bildschirmrand gezeichnet ...(Beachten Sie auch das Fehlen der
()
Unterschale in der obigen Befehlsersetzung.)Technisch gesehen sind Namespaces und Disziplinen nicht genau dasselbe (da Disziplinen so definiert werden können, dass sie entweder global oder lokal auf einen bestimmten Namespace angewendet werden ) , aber sie sind sowohl Bestandteil der Konzeptualisierung von Shell-Datentypen, die für sie von grundlegender Bedeutung ist
ksh93
.Um Ihre speziellen Beispiele anzusprechen:
...oder...
quelle
Ich habe eine POSIX - Shell - Funktion geschrieben , die verwendet werden könnten , um lokal eine Shell builtin oder Funktion in einem Namespace
ksh93
,dash
,mksh
, oderbash
(speziell genannt , weil ich persönlich es in all dieser Arbeit bestätigt habe) . Von den Shells, in denen ich es getestet habe, hat es nur meine Erwartungen nicht erfülltyash
, und ich habe nie erwartet, dass es überhaupt funktioniertzsh
. Ich habe nicht getestetposh
. Ich habe vorposh
einiger Zeit jede Hoffnung aufgegeben und sie seit einiger Zeit nicht mehr installiert. Vielleicht funktioniert es inposh
...?Ich sage, es ist POSIX, weil es beim Lesen der Spezifikation ein bestimmtes Verhalten eines grundlegenden Dienstprogramms ausnutzt, aber zugegebenermaßen ist die Spezifikation in dieser Hinsicht vage, und mindestens eine Person ist anscheinend anderer Meinung als ich. Im Allgemeinen hatte ich eine Meinungsverschiedenheit mit diesem, ich habe schließlich festgestellt, dass der Fehler mein eigener ist, und möglicherweise irre ich mich auch diesmal in Bezug auf die Spezifikation, aber als ich ihn weiter befragte, antwortete er nicht.
Wie ich bereits sagte, funktioniert dies definitiv in den oben genannten Schalen, und es funktioniert im Grunde auf folgende Weise:
Der
command
Befehl wird als grundsätzlich verfügbares Dienstprogramm und als eines der$PATH
vorgefertigten Elemente angegeben. Eine der angegebenen Funktionen besteht darin, spezielle integrierte Dienstprogramme beim Aufrufen in eine eigene Umgebung zu packen, und so ...... das Verhalten der beiden oben genannten Befehlszeilenzuweisungen ist spezifikationsgemäß korrekt. Das Verhalten beider Fehlerzustände ist ebenfalls korrekt und wird dort tatsächlich fast vollständig aus der Spezifikation dupliziert. Zuweisungen, die den Befehlszeilen von Funktionen oder speziellen integrierten Funktionen vorangestellt werden, wirken sich auf die aktuelle Shell-Umgebung aus. Ebenso werden Umleitungsfehler als schwerwiegend angegeben, wenn auf einen dieser Fehler hingewiesen wird.
command
wird spezifiziert, um die spezielle Behandlung von speziellen Builtins in diesen Fällen zu unterdrücken, und der Umleitungsfall wird tatsächlich anhand eines Beispiels in der Spezifikation demonstriert.Regelmäßige Buildins
command
werden beispielsweise so spezifiziert, dass sie in einer Subshell-Umgebung ausgeführt werden - was nicht unbedingt die eines anderen Prozesses bedeutet , sondern nur, dass er grundsätzlich nicht von einem zu unterscheiden ist. Die Ergebnisse des Aufrufs eines regulären Buildins sollten immer dem entsprechen, was mit einem ähnlich fähigen$PATH
Befehl erzielt werden kann . Und so...Der
command
Befehl kann jedoch keine Shell-Funktionen aufrufen und kann daher nicht zum Rendern ihrer speziellen Behandlung verwendet werden, wie dies für reguläre integrierte Funktionen möglich ist. Das ist auch spezifiziert. Tatsächlich besagt die Spezifikation, dass ein primäres Dienstprogramm von darincommand
besteht, dass Sie es innerhalb einer Wrapper-Shell-Funktion verwenden können, die nach einem anderen Befehl benannt ist, um diesen anderen Befehl ohne Selbstrekursion aufzurufen, da die Funktion nicht aufgerufen wird. So was:Wenn Sie es dort nicht verwenden würden, würde
command
diecd
Funktion für die Selbstrekursion fast definitiv segfault.Als reguläres Builtin, das spezielle Builtins aufrufen
command
kann, kann dies jedoch in einer Subshell-Umgebung erfolgen . Und so, während aktuelle Shell-Staat im Macht - Stick auf den aktuell Shell definiert - sicherread
ist$var1
und$var2
hat - zumindest die Ergebnisse der Befehlszeile definiert wahrscheinlich soll nicht ...Nun , ob oder nicht
command
‚s Fähigkeit , sowohl eine regelmäßige builtin zu sein und direkt spezielle builtins zu nennen , ist nur eine Art unerwarteter Lücke in Bezug auf Befehlszeile definiert , weiß ich nicht, aber ich weiß , dass zumindest die vier Schalen bereits erwähnte Ehre dencommand
Namespace.Und obwohl
command
Shell-Funktionen nicht direkt aufgerufen werden können, kann eseval
wie gezeigt und indirekt aufgerufen werden . Also habe ich einen Namespace-Wrapper für dieses Konzept erstellt. Es braucht eine Liste von Argumenten wie:... außer dass das
command
obige Wort nur dann als eins erkannt wird, wenn es mit einem Leerzeichen gefunden werden kann$PATH
. Neben lokal Scoping Shell - Variablen auf der Kommandozeile genannt, sondern auch lokal Tive alle Variable mit einzelnem Klein alphabetischem Namen und eine Liste von anderen Standard - one, wie$PS3
,$PS4
,$OPTARG
,$OPTIND
,$IFS
,$PATH
,$PWD
,$OLDPWD
und einig anderes.Und ja, durch lokales Scoping der
$PWD
und$OLDPWD
-Variablen und anschließendes explizitescd
Verweisen auf$OLDPWD
und$PWD
es kann auch das aktuelle Arbeitsverzeichnis ziemlich zuverlässig erfasst werden. Dies ist nicht garantiert, obwohl es ziemlich anstrengend ist. Es behält einen Deskriptor für7<.
und wenn sein Wrap-Ziel zurückkehrt, tut es diescd -P /dev/fd/7/
. Wenn das aktuelle Arbeitsverzeichnisunlink()
in der Zwischenzeit erstellt wurde, sollte es immer noch gelingen, es wieder zu ändern, aber in diesem Fall wird ein hässlicher Fehler ausgegeben. Und weil es den Deskriptor beibehält, denke ich nicht, dass ein vernünftiger Kernel zulassen sollte, dass sein Root-Gerät auch nicht gemountet wird (???) .Außerdem werden Shell-Optionen lokal überprüft und in dem Zustand wiederhergestellt, in dem sie gefunden wurden, als das umschlossene Dienstprogramm zurückgegeben wurde. Es wird
$OPTS
besonders dadurch behandelt, dass es eine Kopie in seinem eigenen Bereich verwaltet, der es anfänglich den Wert von zuweist$-
. Nachdem auch alle Zuweisungen in der Befehlszeile verarbeitet wurden, wird diesset -$OPTS
unmittelbar vor dem Aufrufen des Wrap-Ziels ausgeführt. Auf diese Weise können Sie, wenn Sie-$OPTS
in der Befehlszeile definieren, die Shell-Optionen Ihres Wrap-Ziels definieren. Wenn das Ziel zurückkehrt, wird esset +$- -$OPTS
mit einer eigenen Kopie von$OPTS
(die von den Befehlszeilendefinitionen nicht betroffen ist) und alle in den ursprünglichen Zustand zurückversetzen.Natürlich hindert nichts den Aufrufer daran
returrn
, die Funktion über das Wrap-Ziel oder seine Argumente explizit zu verlassen . Dies verhindert eine Wiederherstellung / Bereinigung des Zustands, die sonst versucht würde.Um alles zu tun, muss es drei
eval
tief gehen. Zuerst schließt es sich in einen lokalen Bereich ein, dann liest es von innen Argumente ein, validiert sie auf gültige Shell-Namen und beendet sie mit einem Fehler, wenn es einen findet, der nicht vorhanden ist. Wenn alle Argumente gültig sind und schließlichcommand -v "$1"
true zurückgegeben wird (Rückruf:$PATH
ist an dieser Stelle leer) , werden ineval
der Befehlszeile alle verbleibenden Argumente definiert und an das Wrap-Ziel übergeben (obwohl der Sonderfall fürns
- ignoriert wird, da dies nicht der Fall wäre) nicht sehr nützlich sein, und dreieval
s tief ist mehr als tief genug) .Es funktioniert im Grunde so:
Es gibt einige andere Umleitungen und, und ein paar seltsame Tests mit der Art und Weise zu tun , einige Shells setzen
c
in$-
ablehnen und dann zu akzeptieren , als Optionset
(???) , aber es ist alles untergeordnet, und in erster Linie verwendet , nur von speichern emittierenden unerwünschte Ausgabe und ähnliches in Randfällen. Und so funktioniert es. Es kann diese Dinge tun, weil es seinen eigenen lokalen Bereich einrichtet, bevor es sein umschlossenes Dienstprogramm in einem verschachtelten solchen aufruft.Es ist lang, weil ich hier sehr vorsichtig sein will - drei
evals
sind schwer. Aber damit können Sie tun:Es sollte nicht sehr schwierig sein, einen Schritt weiter zu gehen und den lokalen Bereich des umschlossenen Dienstprogramms dauerhaft zu benennen. Und selbst wie geschrieben definiert es bereits eine
$LOCALS
Variable für das umschlossene Dienstprogramm, die nur aus einer durch Leerzeichen getrennten Liste aller Namen besteht, die es in der Umgebung des umschlossenen Dienstprogramms definiert hat.Mögen:
... was absolut sicher ist -
$IFS
wurde auf seinen Standardwert bereinigt und nur gültige Shell-Namen schaffen es, es$LOCALS
sei denn, Sie setzen es selbst in der Befehlszeile. Und selbst wenn eine geteilte Variable möglicherweise Glob-Zeichen enthält, können Sie inOPTS=f
der Befehlszeile festlegen, dass das umschlossene Dienstprogramm deren Erweiterung verhindert. Auf jeden Fall:Und hier ist die Funktion. Allen Befehlen wird ein Präfix vorangestellt
\
, umalias
Erweiterungen zu vermeiden :quelle