Como posso cancelar a exportação de uma variável sem perder seu valor?

10

Digamos que eu exportei uma variável:

foo=bar
export foo

Agora, gostaria de cancelar a exportação. Ou seja, se eu fizer sh -c 'echo "$foo"', não devo bar. foonão deve aparecer no sh -cambiente de todo. sh -cé apenas um exemplo, uma maneira fácil de mostrar a presença de uma variável. O comando pode ser qualquer coisa - pode ser algo cujo comportamento é afetado simplesmente pela presença da variável em seu ambiente.

Eu posso:

  1. unset a variável e perca
  2. Remova-o usando envpara cada comando:env -u foo sh -c 'echo "$foo"'
    • impraticável se você quiser continuar usando o shell atual por um tempo.

Idealmente, eu gostaria de manter o valor da variável, mas não aparecer em nenhum processo filho, nem mesmo como uma variável vazia.

Eu acho que eu poderia fazer:

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

Isso corre o risco de pisar otherfoo, se já existir.

Este é o único caminho? Existem formas padrão?

muru
fonte
1
Você pode exibir o valor em um arquivo temporário, usando mktempse que é suficiente portátil, e Desativar o valor ea origem do arquivo temporário para atribuir a variável. Pelo menos um arquivo temporário pode ser criado com um nome mais ou menos arbitrário, em contraste com uma variável do shell.
Thomas Dickey 01/01
@Sukminder O sh -ccomando é apenas um exemplo. Tome qualquer comando dentro do qual você não possa desmarcar uma variável em seu lugar, se desejar.
Muru

Respostas:

6

Não existe um caminho padrão.

Você pode evitar o uso de uma variável temporária usando uma função A função a seguir cuida para manter variáveis ​​não definidas e variáveis ​​vazias vazias. No entanto, ele não suporta recursos encontrados em alguns shells, como variáveis ​​somente leitura ou digitadas.

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

No ksh, bash e zsh, você pode não exportar uma variável com typeset +x foo. Isso preserva propriedades especiais, como tipos, por isso é preferível usá-lo. Eu acho que todas as conchas que têm um typesetbuiltin têm typeset +x.

case $(LC_ALL=C type typeset 2>&1) in
  typeset\ *\ builtin) unexport () { typeset +x -- "$@"; };;
  *) unexport () {  };; # code above
esac
Gilles 'SO- parar de ser mau'
fonte
1
Para quem não conhece ${var+foo}, ele avalia foose varestá definido, mesmo que vazio, e nada mais.
muru
Digamos, você tem algum comentário sobre typeset +xvs export -npara as conchas que suportam a primeira? É export -nmais raro ou não preserva algumas propriedades?
Muru
@ muru Se você está escrevendo um script bash, pode usar export -nou typeset +xindiferentemente. Em ksh ou zsh, há apenas typeset +x.
Gilles 'SO- stop be evil'
7

EDIT: Por bashapenas, como apontado nos comentários:

A -nopção para exportremover a exportpropriedade de cada nome. (Veja help export.)

Então, para bash, o comando que você quer é:export -n foo

Curinga
fonte
1
Como é específico do shell (consulte POSIX ), o OP não especificou um shell, mas pediu uma maneira padrão de resolver o problema.
9788 Thomas Dickey
1
@ThomasDickey, não estava ciente disso. Obrigado, atualizado.
Curinga
3

Eu escrevi uma função POSIX semelhante, mas isso não corre o risco de execução arbitrária de código:

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

Ele também manipulará quantos argumentos você desejar para fornecê-lo. Se um argumento for um nome válido que ainda não está definido, ele será ignorado silenciosamente. Se um argumento é um nome ruim, ele grava no stderr e pára conforme o apropriado, embora qualquer nome válido anterior a um inválido em sua linha de comando ainda seja processado.

Eu pensei em outro caminho. Eu gosto muito melhor.

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

Bem, ambos usam muitas das mesmas técnicas. Basicamente, se um shell var estiver desativado, uma referência a ele não será expandida com uma +expansão de parâmetro. Mas se estiver definido - independentemente do seu valor -, uma expansão de parâmetro como: ${parameter+word}será expandida para word- e não para o valor da variável. E assim, as variáveis ​​de shell são testadas e substituídas com êxito.

Eles também podem falhar . Na função superior, se for encontrado um nome incorreto, passo $1para nulo $2e deixo $1nulo, porque a próxima coisa que faço é ter returnêxito se todos os argumentos tiverem sido processados ​​e o loop chegar ao fim ou, se o argumento for inválido, o shell será expanda o $2into $1:?que matará um shell com script e retornará uma interrupção para um interativo enquanto escreve wordpara stderr.

No segundo getopts, as atribuições. E não atribuirá um nome incorreto - em vez disso, escreva; ele escreverá uma mensagem de erro padrão para o stderr. Além disso, ele salva o valor do argumento $OPTARG se o argumento era o nome de uma variável definida em primeiro lugar. Então, depois de fazer getoptstudo o que é necessário, é a expansão de evalum conjunto OPTARGpara a atribuição apropriada.

mikeserv
fonte
2
Um dia desses, estarei na enfermaria de psiquiatria em algum lugar depois de tentar envolver minha cabeça em uma de suas respostas. : D Como a outra resposta sofre com a execução arbitrária de código? você pode dar um exemplo?
muru
3
@ muru - não, a menos que um argumento seja um nome inválido. mas esse não é o problema - o problema é que a entrada não é validada. sim, é necessário que você passe um argumento com um nome estranho para fazê-lo executar um código arbitrário - mas essa é praticamente a base de todos os CVE da história. se você tentar exportum nome estranho, ele não matará o seu computador.
precisa saber é
1
@ muru - oh, e os argumentos podem ser arbitrários: var=something; varname=var; export "$varname"é perfeitamente válido. o mesmo vale unset, e com isso e com o outro, mas no minuto em que o conteúdo dessa "$varname"variável fica louco, pode ser lamentável. e é assim que toda essa bashfalha de exportação de função aconteceu de qualquer maneira.
precisa saber é o seguinte
1
@mikeserv eu acho que você ia ficar muito mais upvotes (pelo menos de mim) se você substituiu o código ofuscado com código que se explica (ou comentados as linhas, pelo menos)
PSkocik
1
@PSkocik - feito.
mikeserv