funções e variáveis do shell com o mesmo nome

3

Em Manual de bash :

Note that shell functions and variables with the same name may result in multiple identically-named entries in the environment passed to the shell’s children. Care should be taken in cases where this may cause a problem.

Como pode bash distinguir "funções e variáveis do shell com o        mesmo nome "?

$  func () { return 3; }; func=4; declare -p func; declare -f func;
declare -- func="4"
func () 
{ 
  return 3
}

Quando "várias entradas de nome idêntico no ambiente            passou para os filhos da concha "aconteceu?

Qual "cuidado" deve ser tomado para qual problema?

Obrigado.

    
por Tim 30.07.2017 / 02:41

2 respostas

4

A história geral: namespaces separados

Geralmente shells distinguem entre variáveis e funções porque são usadas em diferentes contextos. Em suma, um nome é um nome de variável se aparecer depois de um $ , ou como um argumento para builtins como export (sem -f ) e unset (sem -f ). Um nome é um nome de função se aparecer como um comando (após a expansão de alias) ou como um argumento para export -f , unset -f , etc.

Variáveis podem ser exportadas para o ambiente. O nome da variável de ambiente é o mesmo da variável shell (e os valores são os mesmos também).

Com o bash antigo: confusão devido à exportação de função

O Bash, ao contrário da maioria dos outros shells, também pode exportar funções para o ambiente. Como não há indicação de tipo no ambiente, não há como reconhecer se uma entrada no ambiente é uma função ou não, além de analisar o nome ou o valor da variável de ambiente.

Versões mais antigas do bash armazenam uma função no ambiente usando o nome da função como nome e algo que se parece com a definição da função como o valor da função. Por exemplo:

bash-4.1$ foobar () { echo foobar; }
bash-4.1$ export -f foobar
bash-4.1$ env |grep -A1 foobar
foobar=() {  echo foobar
}
bash-4.1$ 

Observe que não há como distinguir uma função cujo código seja { echo foobar; } de uma variável cujo valor seja () { echo foobar␤} (onde é um caractere de nova linha). Isso acabou sendo uma má decisão de design.

Às vezes, os shell scripts são chamados com variáveis de ambiente cujo valor está sob controle de uma entidade potencialmente hostil. Scripts CGI, por exemplo. O recurso de exportação / importação de funções do Bash permitia injetar funções dessa maneira. Por exemplo, executando o script

#!/bin/bash
ls

de uma solicitação remota é seguro, desde que o ambiente não contenha variáveis com um determinado nome (como PATH ). Mas se a solicitação puder definir a variável de ambiente ls to () { cat /etc/passwd; } , então o bash executará com alegria cat /etc/passwd , já que esse é o corpo da função ls .

Com o mais recente bash: confusão principalmente aliviada

Esta vulnerabilidade de segurança foi descoberta por Stéphane Chazelas como um dos aspectos do Bug do shellshock . Nas versões pós-Shellshock do bash, as funções exportadas são identificadas pelo nome e não pelo seu conteúdo.

bash-4.3$ foobar () { echo foobar; }
bash-4.3$ export -f foobar
bash-4.3$ env |grep -A1 foobar
BASH_FUNC_foobar%%=() {  echo foobar
}

Não há nenhum problema de segurança agora porque nomes como BASH_FUNC_foobar%% não são comumente usados como nomes de comando e podem ser filtrados por interfaces que permitem a passagem de variáveis de ambiente. É tecnicamente possível ter um caractere % no nome de uma variável de ambiente (isso é o que faz as funções exportadas do bash moderno funcionar), mas normalmente as pessoas não fazem isso porque os shells não aceitam % no nome de um variável.

A frase no manual bash refere-se ao comportamento antigo (pré-Shellshock). Deve ser atualizado ou removido. Com as versões modernas do bash, não há ambiguidade no ambiente se você assumir que as variáveis de ambiente não terão um nome que termine em %% .

    
por 31.07.2017 / 02:37
2

Uma coisa semelhante acontece no Emacs Lisp. Tem dois namespaces, um para funções e outro para variáveis. Se você desreferenciar um símbolo no contexto da função ( (var) ), ele irá chamar a função, se você a defreitar no contexto da variável ( var , ou seja, sem os colchetes), ela fornecerá a variável. Por exemplo:

(defun myvar (myvar)
  "adds 3 to MYVAR"
  (+ 3 myvar))
(setq myvar 7)
(message (myvar myvar))

Será executada a função myvar com o argumento 7 , que é o cancelamento de referência da variável myvar .

Isso pode se tornar muito confuso se você não estiver acostumado.

Depois de analisar sua pergunta e fazer os testes para o bash , estou surpreso que ela apresente o mesmo comportamento. Traduzindo o ELisp de cima para o bash:

[grochmal@phoenix ~]$ myvar () { echo $(($1+3)); }
[grochmal@phoenix ~]$ myvar=7
[grochmal@phoenix ~]$ myvar $myvar
10

Bash é um pouco menos confuso do que ELisp porque você precisa do $ para marcar a variável. Ainda assim, isso pode parecer um declare de um único nome contendo duas coisas. Veja:

[grochmal@phoenix ~]$ declare -p myvar
declare -x myvar="7"
[grochmal@phoenix ~]$ declare -f myvar
myvar () 
{ 
    echo $(($1+3))
}

(P.S. Uma vez que você se acostumar com a existência de dois namespaces, por exemplo, você programa no ELisp por um tempo, isso deixa de se tornar confuso)

    
por 30.07.2017 / 03:26