197

I have a Bash script that builds a string to run as a command

Script:

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

which does not seem to supply the arguments correctly to the $serverbin.

Script output:

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'

if I just paste the command /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' (in the output after "runnning: ") it works fine.

030
  • 10,842
  • 12
  • 78
  • 123
João Portela
  • 6,338
  • 7
  • 38
  • 51
  • Because http://mywiki.wooledge.org/BashFAQ/050 – tripleee Jul 17 '16 at 08:18
  • Note that in some cases, you need to do: `echo | whateverCommands` instead of just `whateverCommands` (for instance, I had to do it like this: `| tail -\`echo | whateverCommands\``) – Andrew Sep 13 '17 at 21:45

8 Answers8

346

You can use eval to execute a string:

eval $illcommando
Arne Burmeister
  • 20,046
  • 8
  • 53
  • 94
  • 2
    Does eval pass forward the command return value (**return value**, not string output)? – Tomáš Zato Apr 21 '15 at 19:53
  • 6
    `eval` is an evil command in all programming languages so use it with caution. – Krishnadas PC Jul 17 '18 at 06:50
  • 3
    `eval "$illcommando"`, **with the quotes**, to not have your command mangled before it's run. Try `eval`ing with a value of `illcommando='printf "%s\n" " * "'` with and without the quotes to see the difference. – Charles Duffy Jul 27 '19 at 02:54
  • @TomášZato-ReinstateMonica Yes, it does forward the return value of the command. However, it does NOT set the `"${PIPESTATUS[@]}"` variable in bash properly. However, `set -o pipefail` does properly cause `eval` to return a failure exit code if the command contains failing command piped to a successful command. – Cameron Hudson Mar 13 '20 at 22:44
  • I was trying to do something like `grep -E '$filter' unfiltered.txt > filtered.txt`, I had no clue why it worked on command line and why with computed values it does not work in bash script. Your answer saved my script. I'll add here also some nice documented examples I have used to better understand how eval works. https://linuxhint.com/bash_eval_command/ – student0495 Apr 06 '20 at 11:54
  • Solutions suggesting `eval` should always discuss the security implications. https://unix.stackexchange.com/questions/23111/what-is-the-eval-command-in-bash See also [When to wrap quotes around a shell variable](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – tripleee Aug 15 '22 at 05:09
33
your_command_string="..."
output=$(eval "$your_command_string")
echo "$output"
Takman
  • 1,028
  • 10
  • 13
  • Looks interesting, but does not work for me. `$your_command_string` does not get executed. What is the problem? – kklepper May 10 '21 at 09:45
  • 1
    Worked for me. Don't know where to put it, but pay attention, that you don't put a space before the `=` like `your_command_string ="..."` – Cadoiz Jun 15 '21 at 13:56
  • This merely decorates the accepted answer from 2010 with a [useless `echo`](https://www.iki.fi/era/unix/award.html#echo) – tripleee Aug 15 '22 at 05:08
29

I usually place commands in parentheses $(commandStr), if that doesn't help I find bash debug mode great, run the script as bash -x script

Ola
  • 477
  • 3
  • 4
12

don't put your commands in variables, just run it

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
  • 327,991
  • 56
  • 259
  • 343
  • 1
    did just that. but where I had variables that should go just as a single argument i did `"${arg}"`. example: server::team_l_start = "${teamAComm}" – João Portela Mar 01 '10 at 15:25
  • For a lot of debugging tasks, it is a good idea to have the command in a string. Saves me a lot of headaches. – Zane Jul 03 '21 at 23:07
  • There are also a lot of situations where having your command in a string brings on a new and stronger kind of headache. Probably review https://mywiki.wooledge.org/BashFAQ/050 – tripleee Aug 15 '22 at 05:10
3

./me casts raise_dead()

I was looking for something like this, but I also needed to reuse the same string minus two parameters so I ended up with something like:

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

This is something I use to monitor openstack heat stack creation. In this case I expect two conditions, an action 'CREATE' and a status 'COMPLETE' on a stack named "Somestack"

To get those variables I can do something like:

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

Here is my gradle build script that executes strings stored in 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

The lone ")" are kind of ugly. But I have no clue how to fix that asthetic aspect.

KANJICODER
  • 3,611
  • 30
  • 17
0

To see all commands that are being executed by the script, add the -x flag to your shabang line, and execute the command normally:

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

Then if you sometimes want to ignore the debug output, redirect stderr somewhere.

Egal
  • 1,374
  • 12
  • 22
0

For me echo XYZ_20200824.zip | grep -Eo '[[:digit:]]{4}[[:digit:]]{2}[[:digit:]]{2}' was working fine but unable to store output of command into variable. I had same issue I tried eval but didn't got output.

Here is answer for my problem: cmd=$(echo XYZ_20200824.zip | grep -Eo '[[:digit:]]{4}[[:digit:]]{2}[[:digit:]]{2}')

echo $cmd

My output is now 20200824

Mayur Gite
  • 397
  • 4
  • 16