Exécuter une chaîne en tant que commande dans un script Bash

152

J'ai un script Bash qui construit une chaîne à exécuter en tant que commande

Scénario:

#! /bin/bash

matchdir="/home/joao/robocup/runner_workdir/matches/testmatch/"

teamAComm="`pwd`/a.sh"
teamBComm="`pwd`/b.sh"
include="`pwd`/server_official.conf"
serverbin='/usr/local/bin/rcssserver'

cd $matchdir
illcommando="$serverbin include='$include' server::team_l_start = '${teamAComm}' server::team_r_start = '${teamBComm}' CSVSaver::save='true' CSVSaver::filename = 'out.csv'"

echo "running: $illcommando"
# $illcommando > server-output.log 2> server-error.log
$illcommando

qui ne semble pas fournir les arguments correctement au $serverbin.

Sortie de script:

running: /usr/local/bin/rcssserver include='/home/joao/robocup/runner_workdir/server_official.conf' server::team_l_start = '/home/joao/robocup/runner_workdir/a.sh' server::team_r_start = '/home/joao/robocup/runner_workdir/b.sh' CSVSaver::save='true' CSVSaver::filename = 'out.csv'
rcssserver-14.0.1

Copyright (C) 1995, 1996, 1997, 1998, 1999 Electrotechnical Laboratory.
2000 - 2009 RoboCup Soccer Simulator Maintenance Group.


Usage: /usr/local/bin/rcssserver [[-[-]]namespace::option=value]
                                 [[-[-]][namespace::]help]
                                 [[-[-]]include=file]
Options:
    help
        display generic help

    include=file
        parse the specified configuration file.  Configuration files
        have the same format as the command line options. The
        configuration file specified will be parsed before all
        subsequent options.

    server::help
        display detailed help for the "server" module

    player::help
        display detailed help for the "player" module

    CSVSaver::help
        display detailed help for the "CSVSaver" module

CSVSaver Options:
    CSVSaver::save=<on|off|true|false|1|0|>
        If save is on/true, then the saver will attempt to save the
        results to the database.  Otherwise it will do nothing.

        current value: false

    CSVSaver::filename='<STRING>'
        The file to save the results to.  If this file does not
        exist it will be created.  If the file does exist, the results
        will be appended to the end.

        current value: 'out.csv'

si je colle juste la commande /usr/local/bin/rcssserver include='/home/joao/robocup/runner_workdir/server_official.conf' server::team_l_start = '/home/joao/robocup/runner_workdir/a.sh' server::team_r_start = '/home/joao/robocup/runner_workdir/b.sh' CSVSaver::save='true' CSVSaver::filename = 'out.csv'(dans la sortie après "runnning:") cela fonctionne très bien.

João Portela
la source
Parce que mywiki.wooledge.org/BashFAQ/050
tripleee
Notez que dans certains cas, vous devez faire: au echo | whateverCommandslieu de simplement whateverCommands(par exemple, je devais le faire comme ceci: | tail -`echo | whateverCommands`)
Andrew

Réponses:

279

Vous pouvez utiliser evalpour exécuter une chaîne:

eval $illcommando
Arne Burmeister
la source
2
Eval transmet-il la valeur de retour de la commande ( valeur de retour , pas de sortie de chaîne)?
Tomáš Zato - Réintégrer Monica le
3
evalest une commande maléfique dans tous les langages de programmation, alors utilisez-la avec prudence.
Krishnadas PC
1
eval "$illcommando", avec les guillemets , pour que votre commande ne soit pas mutilée avant son exécution. Essayez evalavec une valeur de illcommando='printf "%s\n" " * "'avec et sans les guillemets pour voir la différence.
Charles Duffy
@ TomášZato-ReinstateMonica Oui, il transmet la valeur de retour de la commande. Cependant, il ne définit PAS "${PIPESTATUS[@]}"correctement la variable dans bash. Cependant, set -o pipefailprovoque correctement le evalrenvoi d'un code de sortie d'échec si la commande contient une commande en échec dirigée vers une commande réussie.
Cameron Hudson
J'essayais de faire quelque chose comme grep -E '$filter' unfiltered.txt > filtered.txt, je n'avais aucune idée pourquoi cela fonctionnait en ligne de commande et pourquoi avec des valeurs calculées cela ne fonctionnait pas dans le script bash. Votre réponse a sauvé mon script. J'ajouterai ici aussi de jolis exemples documentés que j'ai utilisés pour mieux comprendre comment fonctionne eval. linuxhint.com/bash_eval_command
student0495
28

Je place généralement les commandes entre parenthèses $(commandStr), si cela ne m'aide pas, je trouve le mode de débogage bash excellent, exécutez le script commebash -x script

Ola
la source
4
Je ne sais pas à quoi fait référence commandStr, mais au moins cela n'a pas fonctionné pour moi. Il est préférable d'utiliser des exemples de travail complets.
Robin Manoli
@RobinManoli Amélioration de la clarté pour vous.
Andrew
18
your_command_string="..."
output=$(eval "$your_command_string")
echo "$output"
Takman
la source
10

ne mettez pas vos commandes dans des variables, exécutez-le simplement

matchdir="/home/joao/robocup/runner_workdir/matches/testmatch/"
PWD=$(pwd)
teamAComm="$PWD/a.sh"
teamBComm="$PWD/b.sh"
include="$PWD/server_official.conf"
serverbin='/usr/local/bin/rcssserver'    
cd $matchdir
$serverbin include=$include server::team_l_start = ${teamAComm} server::team_r_start=${teamBComm} CSVSaver::save='true' CSVSaver::filename = 'out.csv'
ghostdog74
la source
1
fait juste cela. mais là où j'avais des variables qui devraient aller comme un seul argument, je l'ai fait "${arg}". exemple: server :: team_l_start = "$ {teamAComm}"
João Portela
1

./me lance lever_dead ()

Je cherchais quelque chose comme ça, mais je devais également réutiliser la même chaîne moins deux paramètres, donc je me suis retrouvé avec quelque chose comme:

my_exe ()
{
    mysql -sN -e "select $1 from heat.stack where heat.stack.name=\"$2\";"
}

C'est quelque chose que j'utilise pour surveiller la création de la pile de chaleur openstack. Dans ce cas j'attends deux conditions, une action 'CREATE' et un statut 'COMPLETE' sur une pile nommée "Somestack"

Pour obtenir ces variables, je peux faire quelque chose comme:

ACTION=$(my_exe action Somestack)
STATUS=$(my_exe status Somestack)
if [[ "$ACTION" == "CREATE" ]] && [[ "$STATUS" == "COMPLETE" ]]
...
cmyster
la source
0

Voici mon script de construction gradle qui exécute les chaînes stockées dans heredocs :

current_directory=$( realpath "." )
GENERATED=${current_directory}/"GENERATED"
build_gradle=$( realpath build.gradle )

## touch because .gitignore ignores this folder:
touch $GENERATED

COPY_BUILD_FILE=$( cat <<COPY_BUILD_FILE_HEREDOC

    cp 
        $build_gradle 
        $GENERATED/build.gradle

COPY_BUILD_FILE_HEREDOC
)
$COPY_BUILD_FILE

GRADLE_COMMAND=$( cat <<GRADLE_COMMAND_HEREDOC

    gradle run

        --build-file       
            $GENERATED/build.gradle

        --gradle-user-home 
            $GENERATED  

        --no-daemon

GRADLE_COMMAND_HEREDOC
)
$GRADLE_COMMAND

Les seuls ")" sont plutôt moche. Mais je n'ai aucune idée de la façon de résoudre cet aspect esthétique.

JMI MADISON
la source
0

Pour voir toutes les commandes exécutées par le script, ajoutez l' -xindicateur à votre ligne shabang et exécutez la commande normalement:

#! /bin/bash -x

matchdir="/home/joao/robocup/runner_workdir/matches/testmatch/"

teamAComm="`pwd`/a.sh"
teamBComm="`pwd`/b.sh"
include="`pwd`/server_official.conf"
serverbin='/usr/local/bin/rcssserver'

cd $matchdir
$serverbin include="$include" server::team_l_start="${teamAComm}" server::team_r_start="${teamBComm}" CSVSaver::save='true' CSVSaver::filename='out.csv'

Ensuite, si vous voulez parfois ignorer la sortie de débogage, redirigez stderrquelque part.

Yigal
la source