Wie kann ich den Export einer Variablen aufheben, ohne ihren Wert zu verlieren?

9

Angenommen, ich habe eine Variable exportiert:

foo=bar
export foo

Jetzt möchte ich den Export aufheben. Das heißt, wenn ich es tue, sh -c 'echo "$foo"'sollte ich es nicht bekommen bar. foosollte überhaupt nicht in sh -cder Umgebung auftauchen . sh -cist nur ein Beispiel, eine einfache Möglichkeit, das Vorhandensein einer Variablen anzuzeigen. Der Befehl kann alles sein - es kann etwas sein, dessen Verhalten einfach durch das Vorhandensein der Variablen in ihrer Umgebung beeinflusst wird.

Ich kann:

  1. unset die Variable und verlieren sie
  2. Entfernen Sie es mit envjedem Befehl:env -u foo sh -c 'echo "$foo"'
    • unpraktisch, wenn Sie die aktuelle Shell noch eine Weile verwenden möchten.

Im Idealfall möchte ich den Wert der Variablen beibehalten, aber nicht in einem untergeordneten Prozess anzeigen lassen, auch nicht als leere Variable.

Ich denke ich könnte tun:

otherfoo="$foo"; unset foo; foo="$otherfoo"; unset otherfoo

Dies kann zu Stampfen führen otherfoo, falls es bereits vorhanden ist.

Ist das der einzige Weg? Gibt es Standardmethoden?

muru
quelle
1
Sie können den Wert in eine temporäre Datei Echo, unter Verwendung , mktempwenn das tragbare genug ist, und unset den Wert und Quelle die temporäre Datei die Variable zuzuweisen. Im Gegensatz zu einer Shell-Variablen kann zumindest eine temporäre Datei mit einem mehr oder weniger beliebigen Namen erstellt werden.
Thomas Dickey
@Sukminder Der sh -cBefehl ist nur ein Beispiel. Nehmen Sie einen Befehl an, innerhalb dessen Sie eine Variable nicht deaktivieren können, wenn Sie so wollen.
Muru

Antworten:

6

Es gibt keinen Standardweg.

Sie können die Verwendung einer temporären Variablen mithilfe einer Funktion vermeiden. Die folgende Funktion sorgt dafür, dass nicht gesetzte Variablen nicht gesetzt und leere Variablen leer bleiben. Es werden jedoch keine Funktionen unterstützt, die in einigen Shells enthalten sind, z. B. schreibgeschützte oder typisierte Variablen.

unexport () {
  while [ "$#" -ne 0 ]; do
    eval "set -- \"\${$1}\" \"\${$1+set}\" \"\$@\""
    if [ -n "$2" ]; then
      unset "$3"
      eval "$3=\$1"
    fi
    shift; shift; shift
  done
}
unexport foo bar

In ksh, bash und zsh können Sie eine Variable mit nicht exportieren typeset +x foo. Dadurch bleiben spezielle Eigenschaften wie Typen erhalten, daher ist es vorzuziehen, sie zu verwenden. Ich denke, dass alle Muscheln, die einen typeseteingebauten haben typeset +x.

case $(LC_ALL=C type typeset 2>&1) in
  typeset\ *\ builtin) unexport () { typeset +x -- "$@"; };;
  *) unexport () {  };; # code above
esac
Gilles 'SO - hör auf böse zu sein'
quelle
1
Für diejenigen, die nicht vertraut sind ${var+foo}, wird ausgewertet, fooob vargesetzt ist, auch wenn es leer ist, und nichts anderes.
Muru
Sagen Sie, haben Sie Kommentare zu typeset +xvs export -nfür die Muscheln, die die ersteren unterstützen? Ist export -nseltener oder bewahrt es einige Eigenschaften nicht?
Muru
@muru Wenn Sie ein Bash-Skript schreiben, können Sie es verwenden export -noder typeset +xgleichgültig. In ksh oder zsh gibt es nur typeset +x.
Gilles 'SO - hör auf böse zu sein'
7

EDIT: Für bashnur, wie in den Kommentaren darauf hingewiesen:

Die -nOption zum exportEntfernen der exportEigenschaft aus jedem angegebenen Namen. (Siehe help export.)

Also für bash, ist der Befehl , den Sie wünschen:export -n foo

Platzhalter
quelle
1
Das ist Shell-spezifisch (siehe POSIX ). OP hat keine Shell angegeben, sondern nach einer Standardmethode zur Lösung des Problems gefragt .
Thomas Dickey
1
@ ThomasDickey, war sich dessen nicht bewusst. Danke, aktualisiert.
Wildcard
3

Ich habe eine ähnliche POSIX-Funktion geschrieben, aber dies riskiert keine willkürliche Codeausführung:

unexport()
    while case ${1##[0-9]*} in                   ### rule out leading numerics
          (*[!_[:alnum:]]*|"")                   ### filter out bad|empty names
          set "" ${1+"bad name: '$1'"}           ### prep bad name error
          return ${2+${1:?"$2"}}                 ### fail w/ above err or return 
          esac
    do    eval  set '"$'"{$1+$1}"'" "$'"$1"'" "$'@\" ###  $1 = (  $1+ ? $1 : "" )
          eval  "${1:+unset $1;$1=\$2;} shift 3"     ### $$1 = ( $1:+ ? $2 : -- )
    done

Es werden auch so viele Argumente verarbeitet, wie Sie möchten. Wenn ein Argument ein gültiger Name ist, der nicht bereits festgelegt wurde, wird es stillschweigend ignoriert. Wenn ein Argument ein falscher Name ist, schreibt es in stderr und hält entsprechend an, obwohl jeder gültige Name, der in der Befehlszeile vor einem ungültigen Namen steht, weiterhin verarbeitet wird.

Ich dachte an einen anderen Weg. Ich mag es viel besser.

unexport()
        while   unset OPTARG; OPTIND=1           ### always work w/ $1
                case  ${1##[0-9]*}    in         ### same old same old
                (*[!_[:alnum:]]*|"")             ### goodname && $# > 0 || break
                    ${1+"getopts"} : "$1"        ### $# ? getopts : ":"
                    return                       ### getopts errored or ":" didnt
                esac
        do      eval   getopts :s: '"$1" -"${'"$1+s}-\$$1\""
                eval   unset  "$1;  ${OPTARG+$1=\${OPTARG}#-}"
                shift
        done

Nun, beide verwenden viele der gleichen Techniken. Wenn eine Shell-Variable nicht gesetzt ist, wird ein Verweis darauf nicht mit einer +Parametererweiterung erweitert. Wenn es jedoch - unabhängig von seinem Wert - festgelegt ist, wird eine Parametererweiterung wie: ${parameter+word}auf word- und nicht auf den Wert der Variablen erweitert. Und so testen sich Shell-Variablen beim Erfolg selbst und ersetzen sie selbst.

Sie können auch selbst versagen . Wenn in der obersten Funktion ein falscher Name gefunden wird, gehe ich $1zu null $2und lasse $1null, da das nächste, was ich mache, entweder returnErfolg ist, wenn alle Argumente verarbeitet wurden und die Schleife beendet ist, oder wenn das Argument ungültig war, wird die Shell dies tun Erweitern Sie das, $2in $1:?das eine Skript-Shell beendet wird, und geben Sie beim Schreiben wordan stderr einen Interrupt an einen interaktiven zurück .

Im zweiten getoptserledigt man die Aufgaben. Und es wird kein schlechter Name zugewiesen - stattdessen wird eine Standardfehlermeldung an stderr geschrieben. Außerdem wird der Wert des Args gespeichert, $OPTARG wenn das Argument in erster Linie der Name einer festgelegten Variablen war. Nachdem Sie dies getan getoptshaben, müssen Sie lediglich die evalMenge eines Sets OPTARGin die entsprechende Zuordnung erweitern.

mikeserv
quelle
2
Eines Tages werde ich irgendwo in der psychiatrischen Abteilung sein, nachdem ich versucht habe, eine Ihrer Antworten in den Kopf zu bekommen. : D Wie leidet die andere Antwort unter der Ausführung von willkürlichem Code? Können Sie ein Beispiel geben?
Muru
3
@muru - nur wenn ein Argument ein ungültiger Name ist. Aber das ist nicht das Problem - das Problem ist, dass die Eingabe nicht validiert wird. Ja, es ist erforderlich, dass Sie ihm ein Argument mit einem seltsamen Namen übergeben, damit er beliebigen Code ausführt - aber das ist so ziemlich die Grundlage für jeden CVE in der Geschichte. Wenn Sie versuchen, exporteinen seltsamen Namen zu finden, wird Ihr Computer nicht zerstört.
Mikesserv
1
@muru - oh, und die Argumente können beliebig sein: var=something; varname=var; export "$varname"ist vollkommen gültig. das gleiche gilt für unsetund mit diesem und dem anderen, aber sobald der Inhalt dieser "$varname"Variablen verrückt wird, könnte es bedauerlich sein. und so bashpassierte das ganze Debakel des Exports von Funktionen sowieso.
Mikeserv
1
@mikeserv Ich denke, Sie würden viel mehr Upvotes bekommen (zumindest von mir), wenn Sie diesen verschleierten Code durch Code ersetzen würden, der sich selbst erklärt (oder zumindest die Zeilen kommentiert)
PSkocik
1
@PSkocik - fertig.
Mikeserv