Gostaria de passar parâmetros para um script bash, no estilo dd. Basicamente eu quero
./script a=1 b=43
ter o mesmo efeito que
a=1 b=43 ./script
Eu pensei que poderia conseguir isso com:
for arg in "$@"; do
eval "$arg";
done
Qual é uma boa maneira de garantir que a eval
segurança seja segura, ou seja, que "$arg"
corresponda a uma atribuição estática (sem execução de código), variável?
Ou há uma maneira melhor de fazer isto? (Eu gostaria de manter isso simples).
=
separador e fazer a tarefa com uma avaliação mais cuidadosamente construída. Apenas por segurança, para uso privado, eu faria como você fez.Respostas:
Você pode fazer isso no bash sem eval (e sem escape artificial):
Edit: Com base em um comentário de Stéphane Chazelas, adicionei flags ao declare para evitar que a variável atribuída já fosse declarada como uma matriz ou variável inteira, o que evitará vários casos em que
declare
avaliará a parte do valor dokey=val
argumento. (+a
Isso causará um erro se a variável a ser definida já estiver declarada como variável de matriz, por exemplo.) Todas essas vulnerabilidades estão relacionadas ao uso dessa sintaxe para reatribuir variáveis existentes (matriz ou número inteiro), que normalmente seriam bem conhecidas. variáveis de shell.De fato, essa é apenas uma instância de uma classe de ataques de injeção que afetará igualmente as
eval
soluções baseadas em: seria realmente muito melhor permitir apenas nomes de argumentos conhecidos do que definir cegamente a variável que estivesse presente na linha de comando. (Considere o que acontece se a linha de comando definirPATH
, por exemplo. Ou redefinePS1
para incluir alguma avaliação que ocorrerá na próxima exibição de prompt.)Em vez de usar variáveis bash, prefiro usar uma matriz associativa de argumentos nomeados, que é mais fácil de definir e muito mais seguro. Como alternativa, ele pode definir variáveis reais do bash, mas apenas se seus nomes estiverem em uma matriz associativa de argumentos legítimos.
Como um exemplo da última abordagem:
fonte
^[[:alpha:]_][[:alnum:]_]*=
:?foo=
é a única maneira de definir foo para a string vazia, por isso deve ser permitido (IMHO). Eu consertei os suportes, obrigado.declare
é tão perigoso quantoeval
(pode-se dizer pior ainda, pois não é tão aparente que seja tão perigoso). Tente, por exemplo, chamar isso com'DIRSTACK=($(echo rm -rf ~))'
como argumento.+x
"não é-x
".-a
= matriz indexada,-A
= matriz associativa,-i
= variável inteira. Assim: matriz não indexada, matriz não associativa, não número inteiro.bash
, talvez seja necessário adicionar+c
para desativar variáveis compostas ou+F
desativar variáveis flutuantes. Eu ainda usariaeval
onde você sabe onde está.Um POSIX (define em
$<prefix>var
vez de$var
evitar problemas com variáveis especiais comoIFS
/PATH
...):Chamado como
myscript x=1 PATH=/tmp/evil %=3 blah '=foo' 1=2
, ele atribuiria:fonte
A solução do lcd047 foi refatorada com um
DD_OPT_
prefixo codificado :frostschutz merece o crédito pela maior parte da refatoração.
Coloquei isso em um arquivo de origem com como variável global:
eval "$DD_OPTS_PARSE"
faz toda a mágica.Uma versão para funções seria:
DD_OPTS_PARSE_LOCAL="${PARSE_AND_REMOVE_DD_OPTS/DD_OPT_/local DD_OPT_}"
Em uso:
eval "$DD_OPTS_PARSE_LOCAL"
Fiz um repo disso, completo com testes e um README.md. Em seguida, usei isso em um wrapper da API do Github API que estava escrevendo e usei o mesmo wrapper para configurar um clone do github do referido repositório (o bootstrapping é divertido).
Passagem segura de parâmetros para scripts bash em apenas uma linha. Apreciar. :)
fonte
*=*
e parar de substituir key / val onde não houver =. (desde que você estava refatorando): Pkey
eval
, e apenas escrevereval "${1%%=*}"=\${1#*=}
. Mas isso é praticamente o mais longe possível,eval "$1"
pois o @ rici'sdeclare "$arg"
não funcionará, obviamente. Também tenha cuidado ao definir coisas comoPATH
ouPS1
.case
. Provavelmente não importa, mas apenas no caso de você não sabia ...Shell Bourne clássico suportado e shell Bash e Korn ainda suportam, uma
-k
opção. Quando está em vigor, qualquerdd
opção de comando '-like' em qualquer lugar da linha de comando é convertida automaticamente em variáveis de ambiente passadas para o comando:É um pouco mais difícil convencer que são variáveis de ambiente; executando isso funciona para mim:
O primeiro
env | grep
não demonstra variáveis de ambiente com uma única letra minúscula. O primeirobash
mostra que não há argumentos passados para o script executado via-c
, e o ambiente contém as três variáveis de letra única. Aset +k
cancela a-k
, e mostra que o mesmo comando agora tem argumentos passados a ele. (Oa=1
foi tratado como$0
no script; você também pode provar isso com eco apropriado.)Isso alcança o que a pergunta faz - que digitar
./script.sh a=1 b=2
deve ser o mesmo que digitara=1 b=2 ./script.sh
.Esteja ciente de que você terá problemas se tentar truques como este em um script:
O
"$@"
é tratado literalmente; não é re-analisado para encontrar variáveis no estilo de atribuição (em ambosbash
eksh
). Eu tentei:e apenas a
already_invoked_with_minus_k
variável de ambiente é definida noexec
script 'd.fonte
Minha tentativa:
Trabalha com (quase) lixo arbitrário no RHS:
fonte
shift
vez deshift 1
). Caso contrário, obrigado!Há algum tempo, decidi
alias
por esse tipo de trabalho. Aqui está outra resposta minha:Às vezes, pode ser possível separar a avaliação e a execução de tais declarações. Por exemplo,
alias
pode ser usado para pré-avaliar um comando. No exemplo a seguir, a definição da variável é salva em um alias que só pode ser declarado com êxito se a$var
variável que está avaliando não contiver bytes que não correspondam aos alfanuméricos ASCII ou _.eval
é usado aqui para manipular a chamada do novo aalias
partir de um contexto de nome de variante citado - não exatamente para a atribuição. Eeval
só é chamado se aalias
definição anterior for bem-sucedida e, embora eu saiba que muitas implementações diferentes aceitem muitos tipos diferentes de valores para nomes de alias, ainda não encontrei um shell que aceite um completamente vazio .A definição no alias é para
_$var
, no entanto, e isso garante que nenhum valor significativo do ambiente seja substituído. Não conheço nenhum valor de ambiente notável que comece com _ e geralmente é uma aposta segura para uma declaração semi-privada.De qualquer forma, se a definição de alias for bem-sucedida, ela declarará um alias nomeado para
$var
o valor de. Eeval
só chamará issoalias
se também não começar com um número - senãoeval
obtém apenas um argumento nulo. Então, se ambas as condições forem atendidaseval
chamadas asalias
e a definição variável guardado naalias
é feito, após o qual o novo alias é prontamente removida da tabela de hash.Também útil
alias
nesse contexto é que você pode imprimir seu trabalho.alias
imprimirá uma declaração duplamente citada de segurança para reexecução quando solicitada.SAÍDA
fonte