Ejecutar una cadena como comando dentro de un script Bash

152

Tengo un script Bash que crea una cadena para ejecutar como comando

Guión:

#! /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

que no parece proporcionar los argumentos correctamente al $serverbin.

Salida 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 solo pego el comando /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'(en la salida después de "runnning:") funciona bien.

João Portela
fuente
Tenga en cuenta que en algunos casos, es necesario hacer: echo | whateverCommandsen lugar de sólo whateverCommands(por ejemplo, tuve que hacerlo de esta manera: | tail -`echo | whateverCommands`)
Andrew

Respuestas:

279

Puede usar evalpara ejecutar una cadena:

eval $illcommando
Arne Burmeister
fuente
2
¿Eval pasa el valor de retorno del comando ( valor de retorno , no salida de cadena)?
Tomáš Zato - Restablece a Mónica el
3
evales un comando malvado en todos los lenguajes de programación, así que úselo con precaución.
Krishnadas PC
1
eval "$illcommando", con las comillas , para que su comando no sea destrozado antes de ejecutarse. Trate evaling con un valor de illcommando='printf "%s\n" " * "'con y sin las comillas para ver la diferencia.
Charles Duffy
@ TomášZato-ReinstateMonica Sí, reenvía el valor de retorno del comando. Sin embargo, NO establece la "${PIPESTATUS[@]}"variable en bash correctamente. Sin embargo, set -o pipefailcausa correctamente evalque devuelva un código de salida de falla si el comando contiene un comando fallido canalizado a un comando exitoso.
Cameron Hudson
Estaba tratando de hacer algo como grep -E '$filter' unfiltered.txt > filtered.txt, no tenía idea de por qué funcionaba en la línea de comandos y por qué con los valores calculados no funciona en el script bash. Tu respuesta salvó mi guión. Agregaré aquí también algunos buenos ejemplos documentados que he usado para comprender mejor cómo funciona eval. linuxhint.com/bash_eval_command
student0495
28

Por lo general, coloco los comandos entre paréntesis $(commandStr), si eso no ayuda, encuentro que el modo de depuración bash es excelente, ejecute el script comobash -x script

Ola
fuente
44
No estoy seguro a qué comando se refiere, pero al menos esto no funcionó para mí. Es mejor si usa ejemplos de trabajo completos.
Robin Manoli
@RobinManoli Mejoró la claridad para ti.
Andrew
18
your_command_string="..."
output=$(eval "$your_command_string")
echo "$output"
Takman
fuente
10

no pongas tus comandos en variables, solo ejecútalo

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
fuente
1
hizo exactamente eso. pero donde tenía variables que deberían ir como un solo argumento lo hice "${arg}". ejemplo: server :: team_l_start = "$ {teamAComm}"
João Portela
1

./me lanza raise_dead ()

Estaba buscando algo como esto, pero también necesitaba reutilizar la misma cadena menos dos parámetros, así que terminé con algo como:

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

Esto es algo que uso para monitorear la creación de la pila de calor de stack abierto. En este caso, espero dos condiciones, una acción 'CREAR' y un estado 'COMPLETO' en una pila llamada "Somestack"

Para obtener esas variables puedo hacer algo como:

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

Aquí está mi script de compilación Gradle que ejecuta cadenas almacenadas en 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

Los solitarios ")" son un poco feos. Pero no tengo idea de cómo solucionar ese aspecto estético.

JMI MADISON
fuente
0

Para ver todos los comandos que ejecuta el script, agregue la -xbandera a su línea shabang y ejecute el comando normalmente:

#! /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'

Luego, si a veces desea ignorar la salida de depuración, redirija a stderralguna parte.

Yigal
fuente