Uruchom ciąg jako polecenie w skrypcie Bash

152

Mam skrypt Bash, który buduje ciąg do uruchomienia jako polecenie

Scenariusz:

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

który wydaje się nie dostarczać poprawnie argumentów do $serverbin.

Wyjście skryptu:

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'

jeśli po prostu wkleię polecenie /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'(w danych wyjściowych po „runnning:”), działa dobrze.

João Portela
źródło
Ponieważ mywiki.wooledge.org/BashFAQ/050
tripleee
Należy zauważyć, że w niektórych przypadkach trzeba zrobić: echo | whateverCommandszamiast po prostu whateverCommands(na przykład, miałem to zrobić tak: | tail -`echo | whateverCommands`)
Andrew

Odpowiedzi:

279

Możesz użyć evaldo wykonania ciągu:

eval $illcommando
Arne Burmeister
źródło
2
Czy eval przekazuje dalej komendę zwracającą wartość ( wartość zwracana , a nie łańcuch wyjściowy)?
Tomáš Zato - Przywróć Monikę
3
evaljest złym poleceniem we wszystkich językach programowania, więc używaj go ostrożnie.
Krishnadas PC,
1
eval "$illcommando", używając cudzysłowów , aby nie spowodować zniekształcenia polecenia przed jego uruchomieniem. Spróbuj evalużyć wartości illcommando='printf "%s\n" " * "'z cudzysłowami i bez cudzysłowów, aby zobaczyć różnicę.
Charles Duffy
@ TomášZato-ReinstateMonica Tak, przekazuje wartość zwracaną polecenia. Jednak NIE ustawia "${PIPESTATUS[@]}"poprawnie zmiennej w bash. Jednak set -o pipefailprawidłowo powoduje evalzwrócenie kodu zakończenia niepowodzenia, jeśli polecenie zawiera polecenie niepowodzenia przekazane potokiem do polecenia pomyślnego.
Cameron Hudson
Próbowałem zrobić coś takiego grep -E '$filter' unfiltered.txt > filtered.txt, nie miałem pojęcia, dlaczego działa w linii poleceń i dlaczego z obliczonymi wartościami nie działa w skrypcie bash. Twoja odpowiedź uratowała mój skrypt. Dodam tutaj również kilka ładnie udokumentowanych przykładów, których użyłem, aby lepiej zrozumieć, jak działa eval. linuxhint.com/bash_eval_command
student0495
28

Zwykle umieszczam polecenia w nawiasach $(commandStr), jeśli to nie pomaga, uważam, że tryb debugowania bash jest świetny, uruchom skrypt jakobash -x script

Ola
źródło
4
Nie jestem pewien, do czego odnosi się commandStr, ale przynajmniej to nie zadziałało dla mnie. Lepiej, jeśli użyjesz pełnych przykładów roboczych.
Robin Manoli
@RobinManoli Poprawiono przejrzystość dla Ciebie.
Andrew,
18
your_command_string="..."
output=$(eval "$your_command_string")
echo "$output"
Takman
źródło
10

nie umieszczaj swoich poleceń w zmiennych, po prostu je uruchom

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
źródło
1
właśnie to zrobił. ale gdzie miałem zmienne, które powinny być tak samo, jak pojedynczy argument "${arg}". przykład: server :: team_l_start = "$ {teamAComm}"
João Portela
1

./me rzuca raise_dead ()

Szukałem czegoś takiego, ale musiałem również ponownie użyć tego samego ciągu bez dwóch parametrów, więc skończyło się na czymś takim:

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

To jest coś, czego używam do monitorowania tworzenia stosu ciepła w otwartym stosie. W tym przypadku oczekuję dwóch warunków, akcji „UTWÓRZ” i statusu „ZAKOŃCZONO” na stosie o nazwie „Somestack”

Aby uzyskać te zmienne, mogę zrobić coś takiego:

ACTION=$(my_exe action Somestack)
STATUS=$(my_exe status Somestack)
if [[ "$ACTION" == "CREATE" ]] && [[ "$STATUS" == "COMPLETE" ]]
...
cmyster
źródło
0

Oto mój skrypt budowania gradle, który wykonuje ciągi przechowywane w heredocach :

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

Samotny „)” jest trochę brzydki. Ale nie mam pojęcia, jak naprawić ten estetyczny aspekt.

JMI MADISON
źródło
0

Aby zobaczyć wszystkie polecenia wykonywane przez skrypt, dodaj -xflagę do linii shabang i wykonaj polecenie normalnie:

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

Następnie, jeśli czasami chcesz zignorować wyjście debugowania, przekieruj stderrgdzieś.

Yigal
źródło