Je voudrais passer des paramètres à un script bash, style dd. En gros, je veux
./script a=1 b=43
avoir le même effet que
a=1 b=43 ./script
Je pensais pouvoir y arriver avec:
for arg in "$@"; do
eval "$arg";
done
Quelle est la bonne façon de s’assurer que eval
est sûr, c'est-à-dire qu'il "$arg"
correspond à une affectation de variable statique (pas d'exécution de code)?
Ou existe-t-il une meilleure façon de procéder? (Je voudrais garder cela simple).
=
séparateur et effectuer la tâche avec un eval plus soigneusement construit. Juste pour la sécurité, pour un usage privé, je le ferais comme vous l'avez fait.Réponses:
Vous pouvez le faire en bash sans eval (et sans échappement artificiel):
Edit: Sur la base d'un commentaire de Stéphane Chazelas, j'ai ajouté des drapeaux à la déclaration pour éviter que la variable assignée ne soit déjà déclarée comme un tableau ou une variable entière, ce qui évitera un certain nombre de cas dans lesquels
declare
évaluer la partie valeur de l'key=val
argument. (Le+a
provoquera une erreur si la variable à définir est déjà déclarée comme une variable de tableau, par exemple.) Toutes ces vulnérabilités sont liées à l'utilisation de cette syntaxe pour réaffecter des variables existantes (tableau ou entier), qui seraient généralement bien connues variables shell.En fait, ce n'est qu'une instance d'une classe d'attaques par injection qui affectera également les
eval
solutions basées sur: il serait vraiment bien mieux de n'autoriser que les noms d'arguments connus que de définir aveuglément la variable qui se trouvait être présente dans la ligne de commande. (Considérez ce qui se passe si la ligne de commande est définiePATH
, par exemple. Ou réinitialisePS1
pour inclure une évaluation qui se produira à la prochaine invite.)Plutôt que d'utiliser des variables bash, je préfère utiliser un tableau associatif d'arguments nommés, qui est à la fois plus facile à définir et beaucoup plus sûr. Alternativement, il pourrait définir des variables bash réelles, mais uniquement si leurs noms se trouvent dans un tableau associatif d'arguments légitimes.
Comme exemple de cette dernière approche:
la source
^[[:alpha:]_][[:alnum:]_]*=
:?foo=
est le seul moyen de définir foo sur la chaîne vide, il doit donc être autorisé (à mon humble avis ). J'ai fixé les supports, merci.declare
est à peu près aussi dangereux queeval
(on peut même dire pire car il n'est pas aussi évident qu'il est aussi dangereux). Essayez par exemple d'appeler cela avec'DIRSTACK=($(echo rm -rf ~))'
comme argument.+x
n'est "pas-x
".-a
= tableau indexé,-A
= tableau associatif,-i
= variable entière. Ainsi: pas de tableau indexé, pas de tableau associatif, pas entier.bash
, vous devrez peut-être ajouter+c
pour désactiver les variables composées ou+F
pour désactiver celles variables. J'utiliserais toujourseval
où vous savez où vous en êtes.Un POSIX (défini
$<prefix>var
au lieu de$var
pour éviter les problèmes avec des variables spéciales commeIFS
/PATH
...):Appelé
myscript x=1 PATH=/tmp/evil %=3 blah '=foo' 1=2
, il assignerait:la source
La solution de lcd047 a été refactorisée avec un
DD_OPT_
préfixe codé en dur :frostschutz mérite le crédit pour la plupart de la refactorisation.
Je mets cela dans un fichier source avec comme variable globale:
eval "$DD_OPTS_PARSE"
fait toute la magie.Une version pour les fonctions serait:
DD_OPTS_PARSE_LOCAL="${PARSE_AND_REMOVE_DD_OPTS/DD_OPT_/local DD_OPT_}"
Utilisé:
eval "$DD_OPTS_PARSE_LOCAL"
J'en ai fait un repo , complet avec des tests et un README.md. Ensuite, j'ai utilisé cela dans un wrapper CLI Github API que j'écrivais, et j'ai utilisé le même wrapper pour configurer un clone github dudit dépôt (le bootstrap est amusant).
Passage sécurisé des paramètres pour les scripts bash sur une seule ligne. Prendre plaisir. :)
la source
*=*
et arrêter de remplacer clé / val là où il n'y a pas de =. (puisque vous refactorisez): Pkey
etval
, et simplement écrireeval "${1%%=*}"=\${1#*=}
. Mais c'est à peu près aussi loin que cela va,eval "$1"
car dans @ ricideclare "$arg"
ne fonctionnera pas, évidemment. Méfiez-vous également de définir des choses commePATH
ouPS1
.case
. Cela n'a probablement pas d'importance, mais juste au cas où vous ne le sauriez pas ...Prise en charge du shell Bourne classique et prise en charge du shell Bash et Korn, une
-k
option. Quand elle est en vigueur, toutes lesdd
options de commande " -like" n'importe où sur la ligne de commande sont converties automatiquement en variables d'environnement passées à la commande:Il est un peu plus difficile de convaincre qu'il s'agit de variables d'environnement; exécuter cela fonctionne pour moi:
Le premier
env | grep
ne montre aucune variable d'environnement avec une seule lettre minuscule. Le premierbash
montre qu'aucun argument n'est transmis au script exécuté via-c
et que l'environnement contient les trois variables à lettre unique. Leset +k
annule le-k
et montre que la même commande a désormais des arguments passés. (Le aa=1
été traité comme$0
pour le script; vous pouvez également le prouver avec un écho approprié.)Ceci réalise ce que la question demande - que la frappe
./script.sh a=1 b=2
devrait être la même chose que la frappea=1 b=2 ./script.sh
.Sachez que vous rencontrez des problèmes si vous essayez des astuces comme celle-ci dans un script:
Le
"$@"
est traité mot pour mot; il n'est pas réanalysé pour trouver des variables de style d'affectation (dans les deuxbash
etksh
). J'ai essayé:et seule la
already_invoked_with_minus_k
variable d'environnement est définie dans leexec
script 'd.la source
Ma tentative:
Cela fonctionne avec des ordures (presque) arbitraires sur le RHS:
la source
shift
au lieu deshift 1
). Sinon merci!Il y a quelque temps, je me suis installé
alias
pour ce genre de travail. Voici une autre de mes réponses:Cependant, il peut parfois être possible de séparer l'évaluation et l'exécution de ces déclarations. Par exemple,
alias
peut être utilisé pour pré-évaluer une commande. Dans l'exemple suivant, la définition de variable est enregistrée dans un alias qui ne peut être déclaré avec succès que si la$var
variable qu'elle évalue ne contient aucun octet qui ne correspond pas aux caractères alphanumériques ASCII ou _.eval
est utilisé ici pour gérer l'appel du nouveau àalias
partir d'un contexte varname cité - pas pour l'affectation exactement. Eteval
n'est appelé du tout que si laalias
définition précédente réussit, et bien que je sache que de nombreuses implémentations différentes accepteront de nombreux types de valeurs différents pour les noms d'alias, je n'ai pas encore trouvé de shell qui acceptera une complètement vide .La définition au sein de l'alias est
_$var
cependant pour, et c'est pour s'assurer qu'aucune valeur d'environnement significative n'est écrasée. Je ne connais pas de valeurs d'environnement remarquables commençant par un _ et c'est généralement une valeur sûre pour une déclaration semi-privée.Quoi qu'il en soit, si la définition de l'alias réussit, elle déclarera un alias nommé pour
$var
la valeur de. Eteval
n'appellera cela quealias
s'il ne commence pas non plus par un nombre - sinon, ileval
n'obtient qu'un argument nul. Par conséquent, si les deux conditions sont remplies,eval
laalias
et la définition de variable enregistrée dans laalias
est effectuée, après quoi le nouvel alias est rapidement supprimé de la table de hachage.Aussi au sujet utile
alias
dans ce contexte est que vous pouvez imprimer votre travail.alias
affichera une déclaration de réexécution du coffre-fort pour le shell à double guillemet lorsque cela vous sera demandé.PRODUCTION
la source