Come posso annullare l'esportazione di una variabile, senza perdere il suo valore?


10

Diciamo che ho esportato una variabile:

foo=bar
export foo

Ora, vorrei non esportarlo. Vale a dire, se lo facessi non sh -c 'echo "$foo"'avrei dovuto bar. foonon dovrebbe apparire affatto sh -cnell'ambiente. sh -cè solo un esempio, un modo semplice per mostrare la presenza di una variabile. Il comando potrebbe essere qualsiasi cosa: potrebbe essere qualcosa il cui comportamento è influenzato semplicemente dalla presenza della variabile nel suo ambiente.

Io posso:

  1. unset la variabile e perderla
  2. Rimuovilo usando envper ogni comando:env -u foo sh -c 'echo "$foo"'
    • poco pratico se si desidera continuare a utilizzare la shell corrente per un po '.

Idealmente, vorrei mantenere il valore della variabile, ma non mostrarlo affatto in un processo figlio, nemmeno come variabile vuota.

Immagino di poter fare:

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

Questo rischia di calpestare otherfoo, se esiste già.

E 'l'unica possibilità? Ci sono modi standard?


1
Si potrebbe eco il valore in un file temporaneo, utilizzando mktempse che è abbastanza portatile e disinserire il valore e fonte il file temporaneo per assegnare alla variabile. Almeno un file temporaneo può essere creato con un nome più o meno arbitrario in contrasto con una variabile di shell.
Thomas Dickey,

@Sukminder Il sh -ccomando è solo un esempio. Prendi qualsiasi comando all'interno del quale non puoi disinserire una variabile al suo posto, se vuoi.
Muru,

Risposte:


6

Non esiste un modo standard.

È possibile evitare di utilizzare una variabile temporanea utilizzando una funzione. La seguente funzione si occupa di mantenere le variabili non impostate non impostate e le variabili vuote vuote. Tuttavia, non supporta le funzionalità presenti in alcune shell come variabili di sola lettura o digitate.

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 e zsh, puoi esportare una variabile con typeset +x foo. Ciò conserva proprietà speciali come i tipi, quindi è preferibile usarlo. Penso che tutte le shell che hanno un typesetbuiltin abbiano typeset +x.

case $(LC_ALL=C type typeset 2>&1) in
  typeset\ *\ builtin) unexport () { typeset +x -- "$@"; };;
  *) unexport () {  };; # code above
esac

1
Per coloro che non hanno familiarità ${var+foo}, valuta foose varè impostato, anche se vuoto, e nient'altro.
Muru,

Dì, hai qualche commento su typeset +xvs export -nper le shell che supportano la prima? È export -npiù raro o non conserva alcune proprietà?
Muru,

@muru Se stai scrivendo uno script bash, puoi usarlo export -no typeset +xindifferentemente. In ksh o zsh, c'è solo typeset +x.
Gilles 'SO- smetti di essere malvagio' il

7

EDIT: Per bashsolo, come sottolineato nei commenti:

L' -nopzione per exportrimuovere la exportproprietà da ciascun nome. (Vedi help export.)

Quindi per bash, il comando che si desidera è:export -n foo


1
Questo è specifico della shell (vedi POSIX ), OP non ha specificato una shell, ma ha chiesto un modo standard per risolvere il problema.
Thomas Dickey,

1
@ThomasDickey, non ne ero a conoscenza. Grazie, aggiornato
Wildcard

3

Ho scritto una funzione POSIX simile, ma ciò non rischia l'esecuzione arbitraria di codice:

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

Gestirà anche tutti gli argomenti che ti interessano per fornirlo. Se un argomento è un nome valido che altrimenti non è già impostato, viene silenziosamente ignorato. Se un argomento è un nome errato, scrive su stderr e si interrompe in modo appropriato, anche se qualsiasi nome valido che precede un non valido sulla sua riga di comando verrà comunque elaborato.

Ho pensato ad un altro modo. Mi piace molto meglio

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

Bene, entrambi usano molte delle stesse tecniche. Fondamentalmente se una shell var non è impostata, un riferimento ad essa non si espanderà con l'espansione di un +parametro. Ma se è impostato - indipendentemente dal suo valore - un parametro di espansione come: ${parameter+word}si espanderà a word- e non al valore della variabile. E così le variabili di shell si auto-testano e si auto-sostituiscono in caso di successo.

Possono anche fallire da soli . Nella funzione superiore se un brutto nome viene trovato mi muovo $1in $2e congedo $1nulla perché la prossima cosa che faccio è sia returnsuccesso, se tutti i args sono stati elaborati e il ciclo è giunto al termine, oppure, se l'arg non era valido, la shell espandere il $2in $1:?cui ucciderà una shell con script e restituirà un interrupt a uno interattivo mentre si scrive wordsu stderr.

Nel secondo getoptsfa gli incarichi. E non assegnerà un brutto nome - piuttosto scriverà scriverà un messaggio di errore standard a stderr. Inoltre, salva il valore di arg $OPTARG se l'argomento era il nome di una variabile set in primo luogo. Quindi, dopo aver fatto getoptstutto ciò che è necessario, è l' espansione di evalun set OPTARGnell'assegnazione appropriata.


2
Uno di questi giorni, sarò nel reparto psichiatrico da qualche parte dopo aver cercato di avvolgere la mia testa attorno a una delle tue risposte. : D In che modo l'altra risposta subisce l'esecuzione arbitraria di codice? Puoi fornire un esempio?
Muru,

3
@muru - a meno che un argomento non sia un nome non valido. ma non è questo il problema: il problema è che l'input non è stato validato. sì, è necessario passargli un argomento con un nome strano per farlo eseguire codice arbitrario - ma questa è praticamente la base per ogni CVE nella storia. se cerchi exportun nome strano, non ucciderà il tuo computer.
Mikeserv,

1
@muru - oh, e gli argomenti possono essere arbitrari: var=something; varname=var; export "$varname"è perfettamente valido. lo stesso vale perunset , e con questo e con l'altro, ma nel momento in cui il contenuto di quella "$varname"variabile diventa pazzo, potrebbe essere deplorevole. ed è così che è bashavvenuta comunque la debacle dell'esportazione dell'intera funzione.
Mikeserv,

1
@mikeserv penso che otterresti molti più voti (almeno da parte mia) se sostituissi quel codice offuscato con un codice che spiega se stesso (o almeno commentando le righe)
PSkocik

1
@PSkocik - fatto.
Mikeserv,
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.