Je voudrais stocker une commande à utiliser ultérieurement dans une variable (pas la sortie de la commande, mais la commande elle-même)
J'ai un script simple comme suit:
command="ls";
echo "Command: $command"; #Output is: Command: ls
b=`$command`;
echo $b; #Output is: public_html REV test... (command worked successfully)
Cependant, lorsque j'essaye quelque chose d'un peu plus compliqué, cela échoue. Par exemple, si je fais
command="ls | grep -c '^'";
La sortie est:
Command: ls | grep -c '^'
ls: cannot access |: No such file or directory
ls: cannot access grep: No such file or directory
ls: cannot access '^': No such file or directory
Une idée de la façon dont je pourrais stocker une telle commande (avec des tuyaux / plusieurs commandes) dans une variable pour une utilisation ultérieure?
Réponses:
Utilisez eval:
la source
backticks
. y = $ (eval $ x) mywiki.wooledge.org/BashFAQ/082eval
est une pratique acceptable uniquement si vous faites confiance au contenu de vos variables. Si vous exécutez, disons,x="ls $name | wc"
(ou mêmex="ls '$name' | wc"
), alors ce code est une voie rapide vers les vulnérabilités d'injection ou d'escalade de privilèges si cette variable peut être définie par quelqu'un avec moins de privilèges. (En parcourant tous les sous-répertoires dans/tmp
, par exemple? Vous feriez mieux de faire confiance à chaque utilisateur du système pour ne pas en appeler$'/tmp/evil-$(rm -rf $HOME)\'$(rm -rf $HOME)\'/'
).eval
est un énorme aimant de bogues qui ne devrait jamais être recommandé sans un avertissement sur le risque de comportement d'analyse inattendu (même sans chaînes malveillantes, comme dans l'exemple de @ CharlesDuffy). Par exemple, essayezx='echo $(( 6 * 7 ))'
et ensuiteeval $x
. Vous pourriez vous attendre à ce que cela imprime "42", mais ce ne sera probablement pas le cas. Pouvez-vous expliquer pourquoi cela ne fonctionne pas? Pouvez-vous expliquer pourquoi j'ai dit «probablement»? Si les réponses à ces questions ne sont pas évidentes pour vous, vous ne devriez jamais y touchereval
.set -x
au préalable pour enregistrer les commandes exécutées, ce qui permettra de voir plus facilement ce qui se passe.Ne pas utiliser
eval
! Il présente un risque majeur d'introduire une exécution de code arbitraire.BashFAQ-50 - J'essaie de mettre une commande dans une variable, mais les cas complexes échouent toujours.
Mettez - le dans un tableau et développer tous les mots avec des guillemets doubles
"${arr[@]}"
pour ne pas laisserIFS
diviser les mots en raison de césure de mots .et voir le contenu du tableau à l'intérieur. Le
declare -p
vous permet de voir le contenu du tableau à l'intérieur avec chaque paramètre de commande dans des index séparés. Si l'un de ces arguments contient des espaces, les guillemets à l'intérieur lors de l'ajout au tableau l'empêcheront de se diviser en raison de Word-Splitting.et exécutez les commandes comme
(ou) utiliser une
bash
fonction pour exécuter la commande,et appelez la fonction comme juste
POSIX
sh
n'a pas de tableaux, donc le plus proche est de construire une liste d'éléments dans les paramètres de position. Voici unesh
façon POSIX d'exécuter un programme de messagerieNotez que cette approche ne peut gérer que des commandes simples sans redirections. Il ne peut pas gérer les redirections, les pipelines, les boucles for / while, les instructions if, etc.
Un autre cas d'utilisation courant est lors de l'exécution
curl
avec plusieurs champs d'en-tête et une charge utile. Vous pouvez toujours définir des arguments comme ci-dessous et les invoquercurl
sur le contenu du tableau étenduUn autre exemple,
maintenant que les variables sont définies, utilisez un tableau pour stocker vos arguments de commande
et maintenant faire une expansion citée appropriée
la source
Command=('echo aaa | grep a')
et"${Command[@]}"
, en espérant qu'il exécute littéralement la commandeecho aaa | grep a
. Ce n'est pas le cas. Je me demande s'il existe un moyen sûr de remplacereval
, mais il semble que chaque solution qui a la même forceeval
pourrait être dangereuse. N'est-ce pas?Command() { echo aaa | grep a; }
- après quoi vous pouvez simplement exécuterCommand
, ouresult=$(Command)
, ou autre.En utilisant cette méthode, la commande est immédiatement évaluée et sa valeur de retour est stockée.
Idem avec backtick
Utiliser eval dans le
$(...)
ne le fera pas évaluer plus tardEn utilisant eval, il est évalué lorsqu'il
eval
est utiliséDans l'exemple ci-dessus, si vous devez exécuter une commande avec des arguments, placez-les dans la chaîne que vous stockez
Pour les scripts bash, c'est rarement pertinent, mais une dernière remarque. Soyez prudent avec
eval
. Évaluer uniquement les chaînes que vous contrôlez, jamais les chaînes provenant d'un utilisateur non approuvé ou créées à partir d'une entrée utilisateur non approuvée.la source
eval $stored_date
peut être assez bien lorsqu'ilstored_date
ne contient quedate
, maiseval "$stored_date"
c'est beaucoup plus fiable. Exécutezstr=$'printf \' * %s\\n\' *'; eval "$str"
avec et sans les guillemets autour de la finale"$str"
pour un exemple. :)Pour bash, stockez votre commande comme ceci:
Exécutez votre commande comme ceci:
la source
J'ai essayé différentes méthodes:
Production:
Comme vous pouvez le voir, seul le troisième a
"$@"
donné le résultat correct.la source
Soyez prudent lors de l'enregistrement d'une commande auprès du:
X=$(Command)
Celui-ci est toujours exécuté avant même d'être appelé. Pour vérifier et confirmer cela, vous pouvez faire:
la source
la source
Il n'est pas nécessaire de stocker des commandes dans des variables même si vous devez l'utiliser plus tard. exécutez-le simplement comme d'habitude. Si vous stockez dans une variable, vous auriez besoin d'une sorte d'
eval
instruction ou d'appeler un processus shell inutile pour "exécuter votre variable".la source
var='*.txt'; find . -name "$var"