linux - tutorial - shell script variable




Kann ein Shell-Skript Umgebungsvariablen der aufrufenden Shell setzen? (14)

Anders als schriftliche Bedingungen, abhängig davon, was $ SHELL / $ TERM festgelegt ist, nein. Was ist falsch an der Verwendung von Perl? Es ist ziemlich allgegenwärtig (ich kann mir nicht eine einzige UNIX-Variante vorstellen, die es nicht hat), und es wird Ihnen die Mühe ersparen.

Ich versuche, ein Shell-Skript zu schreiben, das beim Ausführen einige Umgebungsvariablen setzt, die in der Shell des Aufrufers gesetzt bleiben.

setenv FOO foo

in csh / tcsh, oder

export FOO=foo

in sh / bash nur während der Skriptausführung.

Ich weiß schon, dass

source myscript

wird die Befehle des Skripts ausführen, anstatt eine neue Shell zu starten, und dies kann zur Einstellung der Umgebung des "Aufrufers" führen.

Aber hier ist der Haken:

Ich möchte, dass dieses Skript von bash oder csh aufgerufen werden kann. Mit anderen Worten, ich möchte, dass Benutzer beider Shells mein Skript ausführen können und die Umgebung ihrer Shell geändert wird. Daher funktioniert 'source' nicht für mich, da ein Benutzer, der csh ausführt, kein Bash-Skript erstellen kann und ein Benutzer, der bash ausführt, kein csh-Skript erstellen kann.

Gibt es eine vernünftige Lösung, bei der keine TWO-Versionen auf dem Skript geschrieben und gepflegt werden müssen?


Das funktioniert - es ist nicht das, was ich verwenden würde, aber es "funktioniert". Lassen Sie uns einen Skript- teredo erstellen, um die Umgebungsvariable TEREDO_WORMS :

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL -i

Es wird von der Korn-Shell interpretiert, exportiert die Umgebungsvariable und ersetzt sich dann durch eine neue interaktive Shell.

Vor dem Ausführen dieses Skripts haben wir SHELL in der Umgebung auf die C-Shell gesetzt, und die Umgebungsvariable TEREDO_WORMS ist nicht gesetzt:

% env | grep SHELL
SHELL=/bin/csh
% env | grep TEREDO
%

Wenn das Skript ausgeführt wird, befinden Sie sich in einer neuen Shell, einer anderen interaktiven C-Shell, aber die Umgebungsvariable ist festgelegt:

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

Wenn Sie diese Shell verlassen, übernimmt die ursprüngliche Shell:

% exit
% env | grep TEREDO
%

Die Umgebungsvariable ist nicht in der ursprünglichen Shell-Umgebung festgelegt. Wenn Sie exec teredo , um den Befehl auszuführen, wird die ursprüngliche interaktive Shell durch die Korn-Shell ersetzt, die die Umgebung festlegt, und diese wiederum wird durch eine neue interaktive C-Shell ersetzt:

% exec teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

Wenn Sie exit (oder Control-D ) eingeben, wird Ihre Shell beendet und Sie werden möglicherweise aus diesem Fenster ausgeloggt oder Sie kehren zur vorherigen Shell-Ebene zurück, von wo aus die Experimente gestartet wurden.

Derselbe Mechanismus funktioniert für die Bash- oder Korn-Shell. Möglicherweise stellen Sie fest, dass die Eingabeaufforderung nach den Beenden-Befehlen an lustigen Orten angezeigt wird.

Beachten Sie die Diskussion in den Kommentaren. Dies ist keine Lösung, die ich empfehlen würde, aber es erfüllt den angegebenen Zweck eines einzelnen Skripts, um die Umgebung festzulegen, die mit allen Shells funktioniert (die die Option -i akzeptieren, um eine interaktive Shell zu erstellen). Sie können auch "[email protected]" nach der Option hinzufügen, um weitere Argumente weiterzuleiten, wodurch die Shell möglicherweise als allgemeines Werkzeug 'Umgebung festlegen und Befehl ausführen' verwendet werden kann. Sie können das Argument -i weglassen, wenn andere Argumente zu folgendem führen:

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL "${@-'-i'}"

Das Bit "${@-'-i'}" bedeutet "Wenn die Argumentliste mindestens ein Argument enthält, verwenden Sie die ursprüngliche Argumentliste. Andernfalls ersetzen Sie -i für die nicht vorhandenen Argumente.


Die kurze Antwort ist nein, Sie können die Umgebung des übergeordneten Prozesses nicht ändern, aber es scheint, als ob Sie eine Umgebung mit benutzerdefinierten Umgebungsvariablen und der vom Benutzer ausgewählten Shell wünschen.

Also warum nicht einfach so etwas wie

#!/usr/bin/env bash
FOO=foo $SHELL

Dann, wenn Sie mit der Umgebung fertig sind, verlassen Sie einfach.


Eine andere Problemumgehung, die ich nicht erwähnt, ist, den Variablenwert in eine Datei zu schreiben.

Ich stieß auf ein sehr ähnliches Problem, bei dem ich den letzten Set-Test durchführen wollte (anstelle all meiner Tests). Mein erster Plan war, einen Befehl zum Setzen der env-Variablen TESTCASE zu schreiben, und dann einen anderen Befehl zu haben, der dies zum Ausführen des Tests verwenden würde. Unnötig zu sagen, dass ich genau das gleiche Problem hatte wie Sie.

Aber dann kam ich auf diesen einfachen Hack:

Erster Befehl ( testset ):

#!/bin/bash

if [ $# -eq 1 ]
then
  echo $1 > ~/.TESTCASE
  echo "TESTCASE has been set to: $1"
else
  echo "Come again?"
fi

Zweiter Befehl ( testrun ):

#!/bin/bash

TESTCASE=$(cat ~/.TESTCASE)
drush test-run $TESTCASE

Es ist "irgendwie möglich" durch die Verwendung von gdb und setenv(3) , obwohl ich es schwer setenv(3) , dies tatsächlich zu tun. (Das heißt, das neueste ubuntu lässt das nicht zu, ohne dem Kernel zu sagen, dass er ptrace toleranter sein soll, und dasselbe gilt auch für andere Distributionen).

$ cat setfoo
#! /bin/bash

gdb /proc/${PPID}/exe ${PPID} <<END >/dev/null
call setenv("foo", "bar", 0)
END
$ echo $foo

$ ./setfoo
$ echo $foo
bar

Fügen Sie die Markierung -l oben in Ihrem Bash-Skript ein, z

#!/usr/bin/env bash -l

...

export NAME1="VALUE1"
export NAME2="VALUE2"

Die Werte mit NAME1 und NAME2 nun in Ihre aktuelle Umgebung exportiert, diese Änderungen sind jedoch nicht dauerhaft. Wenn Sie möchten, dass sie dauerhaft sind, müssen Sie sie zu Ihrer .bashrc Datei oder einer anderen Init-Datei hinzufügen.

Von den man-Seiten:

-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).

Ich sehe keine Antwort, die dokumentiert, wie dieses Problem mit kooperierenden Prozessen gelöst werden kann. Ein übliches Muster mit Dingen wie ssh-agent besteht darin, dass der Kindprozess einen Ausdruck eval den der Elternteil eval kann.

bash$ eval $(shh-agent)

Zum Beispiel hat ssh-agent Optionen, um Csh- oder Bourne-kompatible Ausgabesyntax auszuwählen.

bash$ ssh-agent
SSH2_AUTH_SOCK=/tmp/ssh-era/ssh2-10690-agent; export SSH2_AUTH_SOCK;
SSH2_AGENT_PID=10691; export SSH2_AGENT_PID;
echo Agent pid 10691;

(Dadurch wird der Agent gestartet, aber Sie können ihn nicht verwenden, es sei denn, Sie kopieren diese Ausgabe jetzt in Ihre Shell-Eingabeaufforderung.)

bash$ ssh-agent -c
setenv SSH2_AUTH_SOCK /tmp/ssh-era/ssh2-10751-agent;
setenv SSH2_AGENT_PID 10752;
echo Agent pid 10752;

(Wie Sie sehen können, verwenden csh und tcsh setenv zum Setzen von Variablen.)

Ihr eigenes Programm kann dies auch tun.

bash$ foo=$(makefoo)

Ihr makefoo Skript würde einfach den Wert berechnen und drucken und den Aufrufer machen lassen, was er will - es einer Variablen zuzuordnen ist ein häufiger Anwendungsfall, aber wahrscheinlich nicht etwas, das Sie in das Werkzeug fest einprogrammieren wollen Wert.


Ihr Shell-Prozess verfügt über eine Kopie der Umgebung des übergeordneten Elements und keinen Zugriff auf die Umgebung des übergeordneten Prozesses. Wenn der Shell-Prozess beendet wird, gehen alle Änderungen verloren, die an der Umgebung vorgenommen wurden. Die Beschaffung einer Skriptdatei ist die am häufigsten verwendete Methode zum Konfigurieren einer Shell-Umgebung. Sie möchten vielleicht nur die Kugel beißen und eine für jeden der beiden Shell-Varianten beibehalten.


Sie können den untergeordneten Prozess anweisen, seine Umgebungsvariablen zu drucken (indem Sie "env" aufrufen), dann die gedruckten Umgebungsvariablen im übergeordneten Prozess durchlaufen und "export" für diese Variablen aufrufen.

Der folgende Code basiert auf der Erfassung der Ausgabe von find. -print0 in ein Bash-Array

Wenn die übergeordnete Shell die Bash ist, können Sie verwenden

while IFS= read -r -d $'\0' line; do
    export "$line"
done < <(bash -s <<< 'export VARNAME=something; env -0')
echo $VARNAME

Wenn die übergeordnete Shell der Bindestrich ist, dann liefert read nicht das -d Flag und der Code wird komplizierter

TMPDIR=$(mktemp -d)
mkfifo $TMPDIR/fifo
(bash -s << "EOF"
    export VARNAME=something
    while IFS= read -r -d $'\0' line; do
        echo $(printf '%q' "$line")
    done < <(env -0)
EOF
) > $TMPDIR/fifo &
while read -r line; do export "$(eval echo $line)"; done < $TMPDIR/fifo
rm -r $TMPDIR
echo $VARNAME

Sie können die Shell des Aufrufers nicht ändern, da sie sich in einem anderen Prozesskontext befindet. Wenn untergeordnete Prozesse die Variablen Ihrer Shell erben, übernehmen sie Kopien selbst.

Eine Sache, die Sie tun können, ist, ein Skript zu schreiben, das die richtigen Befehle für tcsh oder sh ausgibt, wie es aufgerufen wird. Wenn Sie Skript sind, "setit" dann tun Sie:

ln -s setit setit-sh

und

ln -s setit setit-csh

Jetzt entweder direkt oder in einem Alias, Sie tun dies von SH

eval `setit-sh`

oder das von csh

eval `setit-csh`

setit verwendet $ 0, um seinen Ausgabestil zu bestimmen.

Dies erinnert daran, wie Benutzer die Umgebungsvariable TERM festlegen.

Der Vorteil ist, dass Setit nur in der gewünschten Shell geschrieben wird:

#!/bin/bash
arg0=$0
arg0=${arg0##*/}
for nv in \
   NAME1=VALUE1 \
   NAME2=VALUE2
do
   if [ x$arg0 = xsetit-sh ]; then
      echo 'export '$nv' ;'
   elif [ x$arg0 = xsetit-csh ]; then
      echo 'setenv '${nv%%=*}' '${nv##*=}' ;'
   fi
done

Mit den oben angegebenen symbolischen Links und dem eval des Backquotes hat dies das gewünschte Ergebnis.

Um den Aufruf für csh, tcsh oder ähnliche Shells zu vereinfachen:

alias dosetit 'eval `setit-csh`'

oder für sh, bash und dergleichen:

alias dosetit='eval `setit-sh`'

Eine nette Sache dabei ist, dass Sie nur die Liste an einem Ort pflegen müssen. Theoretisch könnte man sogar die Liste in eine Datei cat nvpairfilename und cat nvpairfilename zwischen "in" und "do" setzen.

Dies ist in etwa so, wie die Terminaleinstellungen der Login-Shell vorgenommen wurden: Ein Skript würde Statements ausgeben, die in der Login-Shell ausgeführt werden. Ein Alias ​​würde im Allgemeinen verwendet werden, um den Aufruf einfach zu machen, wie in "tset vt100". Wie in einer anderen Antwort erwähnt, gibt es auch ähnliche Funktionen im INN UseNet News Server.


Sie können immer Aliase verwenden

alias your_env='source ~/scripts/your_env.sh'


Unter OS X bash können Sie Folgendes tun:
Erstellen Sie die Bash-Skriptdatei, um die Variable zu deaktivieren

#!/bin/bash
unset http_proxy

Machen Sie die Datei ausführbar

sudo chmod 744 unsetvar

Erstellen Sie einen Alias

alias unsetvar='source /your/path/to/the/script/unsetvar'

Es sollte einsatzbereit sein, solange Sie den Ordner mit Ihrer Skriptdatei an den Pfad angehängt haben.


Verwenden Sie die Aufrufsyntax "dot space script". Im Folgenden wird beispielsweise beschrieben, wie Sie den vollständigen Pfad zu einem Skript verwenden:

. /path/to/set_env_vars.sh

Und so machen Sie es, wenn Sie sich im selben Verzeichnis wie das Skript befinden:

. set_env_vars.sh

Diese führen das Skript unter der aktuellen Shell aus, anstatt eine andere zu laden (was passieren würde, wenn Sie ./set_env_vars.sh tun ./set_env_vars.sh ). Da die Umgebungsvariablen in derselben Shell ausgeführt werden, sind sie beim Beenden verfügbar.

Das ist die gleiche Sache wie das Aufrufen der source set_env_vars.sh , aber sie ist kürzer beim Typ und könnte an einigen Stellen funktionieren, wo die source dies nicht tut.





tcsh