Execute uma string como um comando em um script Bash

152

Eu tenho um script Bash que cria uma string para executar como um comando

Roteiro:

#! /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 parece não fornecer os argumentos corretamente para o $serverbin.

Saída 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'

se eu apenas colar o 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'(na saída após "runnning:"), ele funcionará bem.

João Portela
fonte
Porque mywiki.wooledge.org/BashFAQ/050
tripleee 17/07
Note-se que, em alguns casos, você precisa fazer: echo | whateverCommandsem vez de apenas whateverCommands(por exemplo, eu tive que fazê-lo como este: | tail -`echo | whateverCommands`)
Andrew

Respostas:

279

Você pode usar evalpara executar uma string:

eval $illcommando
Arne Burmeister
fonte
2
O eval repassa o valor de retorno do comando ( valor de retorno , não a saída da string)?
Tomáš Zato - Restabelece Monica
3
evalé um comando maligno em todas as linguagens de programação, portanto, use-o com cuidado.
Krishnadas PC
1
eval "$illcommando", com as aspas , para não ter seu comando mutilado antes de ser executado. Tente evalusar um valor illcommando='printf "%s\n" " * "'com e sem as aspas para ver a diferença.
Charles Duffy
@ TomášZato-ReinstateMonica Sim, encaminha o valor de retorno do comando. No entanto, ele NÃO define a "${PIPESTATUS[@]}"variável no bash corretamente. No entanto, set -o pipefailcausa corretamente o evalretorno de um código de saída com falha se o comando contiver um comando com falha canalizado para um comando bem-sucedido.
Cameron Hudson
Eu estava tentando fazer algo do tipo grep -E '$filter' unfiltered.txt > filtered.txt: não fazia ideia do porquê funcionava na linha de comando e por que, com valores calculados, não funciona no script bash. Sua resposta salvou meu script. Vou adicionar aqui também alguns bons exemplos documentados que usei para entender melhor como o eval funciona. linuxhint.com/bash_eval_command
student0495 06/04
28

Normalmente coloco comandos entre parênteses $(commandStr), se isso não ajudar, acho ótimo o modo de depuração do bash, execute o script comobash -x script

Ola
fonte
4
Não sei ao que commandStr se refere, mas pelo menos isso não funcionou para mim. É melhor se você usar exemplos de trabalho completos.
22613 Robin Manoli
@RobinManoli Melhorou a clareza para você.
Andrew
18
your_command_string="..."
output=$(eval "$your_command_string")
echo "$output"
Takman
fonte
10

não coloque seus comandos em variáveis, apenas execute

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
fonte
1
fez exatamente isso. mas onde eu tinha variáveis ​​que deveriam ser apenas um argumento único que eu fiz "${arg}". exemplo: server :: team_l_start = "$ {teamAComm}"
João Portela
1

./me lança raise_dead ()

Eu estava procurando por algo assim, mas também precisava reutilizar a mesma string menos dois parâmetros, então acabei com algo como:

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

Isso é algo que eu uso para monitorar a criação de pilha de calor do openstack. Nesse caso, espero duas condições, uma ação 'CREATE' e um status 'COMPLETE' em uma pilha chamada "Somestack"

Para obter essas variáveis, posso fazer algo como:

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

Aqui está o meu script de compilação gradle que executa cadeias armazenadas em 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

Os solitários ")" são meio feios. Mas não tenho idéia de como consertar esse aspecto estético.

JMI MADISON
fonte
0

Para ver todos os comandos que estão sendo executados pelo script, adicione o -xsinalizador à sua linha shabang e execute o 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'

Então, se você desejar ignorar a saída de depuração, redirecione para stderralgum lugar.

Yigal
fonte