Je voudrais stocker une commande à utiliser ultérieurement dans une variable (pas le résultat 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'essaie 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
Avez-vous une idée de la façon dont je pourrais stocker une telle commande (avec des tubes/plusieurs commandes) dans une variable pour une utilisation ultérieure?
Utilisez eval:
x="ls | wc"
eval "$x"
y=$(eval "$x")
echo "$y"
Ne pas utilisez eval
! Il y a un risque majeur d’introduction de l’exécution de code arbitraire.
Placez-le dans un tableau et développez tous les mots avec des guillemets doubles "${arr[@]}"
to not laissez le IFS
diviser les mots en raison de fractionnement de mots .
cmdArgs=()
cmdArgs=('date' '+%H:%M:%S')
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 distincts. Si un tel argument contient des espaces, la citation à l'intérieur lors de l'ajout au tableau l'empêchera d'être scindée en raison de la division de Word.
declare -p cmdArgs
declare -a cmdArgs='([0]="date" [1]="+%H:%M:%S")'
et exécuter les commandes en tant que
"${cmdArgs[@]}"
23:15:18
(ou) utilisez tout simplement une fonction bash
pour exécuter la commande,
cmd() {
date '+%H:%M:%S'
}
et appelez la fonction juste
cmd
POSIX sh
n'a pas de tableaux, vous pouvez donc créer une liste d'éléments dans les paramètres de position. Voici un moyen POSIX sh
d'exécuter un programme de messagerie
# POSIX sh
# Usage: sendto subject address [address ...]
sendto() {
subject=$1
shift
first=1
for addr; do
if [ "$first" = 1 ]; then set --; first=0; fi
set -- "$@" --recipient="$addr"
done
if [ "$first" = 1 ]; then
echo "usage: sendto subject address [address ...]"
return 1
fi
MailTool --subject="$subject" "$@"
}
Notez 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.
var=$(echo "asdf")
echo $var
# => asdf
En utilisant cette méthode, la commande est immédiatement évaluée et sa valeur de retour est stockée.
stored_date=$(date)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 10:57:16 EST 2015
Même avec backtick
stored_date=`date`
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:02:19 EST 2015
Utiliser eval dans la $(...)
ne le fera pas évaluer plus tard
stored_date=$(eval "date")
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015
# (wait a few seconds)
echo $stored_date
# => Thu Jan 15 11:05:30 EST 2015
En utilisant eval, il est évalué lorsque eval
est utilisé
stored_date="date" # < storing the command itself
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:05 EST 2015
# (wait a few seconds)
echo $(eval "$stored_date")
# => Thu Jan 15 11:07:16 EST 2015
# ^^ Time changed
Dans l'exemple ci-dessus, si vous devez exécuter une commande avec des arguments, placez-les dans la chaîne que vous stockez.
stored_date="date -u"
# ...
Pour les scripts bash, cela est rarement pertinent, mais une dernière note. Soyez prudent avec eval
. N'évaluez que les chaînes que vous contrôlez, jamais celles provenant d'un utilisateur non fiable ou construites à partir d'une entrée utilisateur non fiable.