2009-09-04 7 views
2

Je stocke les arguments dans une commande d'une variable. La dernière commande que je veux est:Citation non respectée dans une variable bash

mock -r myconfig --define "debug_package %{nil}" --resultdir results --rebuild mypackage.src.rpm 

Voilà ma tentative:

set -x # for debugging 

RESULTDIR=results 
MOCK_CONFIG="myconfig" 
MOCK_ARGS="-r $MOCK_CONFIG --define \"debug_package %{nil}\" --resultdir $RESULTDIR" 
cmd="mock $MOCK_ARGS --rebuild mypackage.src.rpm" 
$cmd 

Les résultats sont les suivants:

+ RESULTDIR=results 
+ MOCK_CONFIG=myconfig 
+ MOCK_ARGS='-r myconfig --define "debug_package %{nil}" --resultdir results' 
+ cmd='mock -r myconfig --define "debug_package %{nil}" --resultdir results --rebuild mypackage.src.rpm' 
+ mock -r myconfig --define '"debug_package' '%{nil}"' --resultdir results --rebuild mypackage.src.rpm 
ERROR: Bad option for '--define' ("debug_package). Use --define 'macro expr' 

Comme vous pouvez le voir, les arguments au paramètre --define ne sont pas être cité correctement. --define pense que je le passe seulement debug_package, ce qui est incomplet.

J'ai essayé diverses variations dans les guillemets lors de la définition MOCK_ARGS, essayant même d'échapper à l'espace entre debug_package et %{nil}.

Quelle combinaison de guillemets et/ou d'échappements me permet de construire cette liste d'arguments et d'exécuter la commande à partir de ce script?

EDIT:

La raison pour laquelle je suis le stockage de la commande qui en résulte dans une variable est parce qu'il finit par être passé dans une fonction qui fait un peu de journalisation, exécute alors la commande.

En outre, j'ai rencontré this FAQ qui suggère que je devrais utiliser des tableaux au lieu d'une variable. J'ai commencé à expérimenter avec des tableaux mais jusqu'à présent, je n'ai pas de solution de travail.

+0

Cela ressemble à un doublon de http://stackoverflow.com/questions/954390/echo-notation-quotation-marks-in-tcsh –

+0

La réponse à http://stackoverflow.com/questions/954390/echo- nested-quotation-marks-in-tcsh ne fonctionne pas pour moi. –

+0

duplication possible de [Comment exécuter une commande bash stockée sous forme de chaîne avec guillemets et astérisque] (http://stackoverflow.com/questions/2005192/how-to-execute-a-bash-command-stored-as-a -string-with-quotes-and-astterisk) –

Répondre

4

Les tableaux sont la voie à suivre pour des choses comme ça.Voici ce que je suis venu avec:

log_and_run() { 
    echo "$(date): running command: $*" 
    "[email protected]" 
    echo "$1 completed with status $?" 
} 

RESULTDIR=results 
MOCK_CONFIG="myconfig" 
MOCK_ARGS=(-r "$MOCK_CONFIG" --define "debug_package %{nil}" --resultdir "$RESULTDIR") 
cmd=(mock "${MOCK_ARGS[@]}" --rebuild mypackage.src.rpm) 
log_and_run "${cmd[@]}" 
# could also use: log_and_run mock "${MOCK_ARGS[@]}" --rebuild mypackage.src.rpm 

Notez que $ * à tous les paramètres étend séparés par des espaces (adaptés pour passer à une commande de journal); tandis que "$ @" s'étend à tous les paramètres en tant que mots séparés (comme utilisé dans log_and_run, le premier sera traité comme la commande à exécuter, le reste en paramètre); de même, "$ {MOCK_ARGS [@]}" se développe en tous les éléments de MOCK_ARGS en tant que mots séparés, qu'ils contiennent ou non des espaces (donc chaque élément de MOCK_ARGS devient un élément de cmd, sans confusion d'analyse).

Aussi, j'ai cité les expansions de MOCK_CONFIG et RESULTDIR; ceci n'est pas nécessaire ici car ils ne contiennent pas d'espaces, mais c'est une bonne habitude d'entrer au cas où ils contiendraient des espaces (par exemple, est-il passé dans votre script depuis l'extérieur? . Par ailleurs, si vous devez transmettre des paramètres supplémentaires à la fonction (j'utiliserai l'exemple du nom de fichier pour vous connecter), vous pouvez utiliser array slicing pour ne séparer que les paramètres suivants à utiliser comme commande de connexion/exécuter:

log_and_run() { 
    echo "$(date): running command ${*:2}" >>"$1" 
    "${@:2}" 
    echo "$2 completed with status $?" >>"$1" 
} 
#... 
log_and_run test.log "${cmd[@]}" 

Addendum: si vous voulez citer les paramètres contenant l'espace dans le journal, vous pouvez construire « manuellement » la chaîne de journal et faire tout ce que vous voulez citer. Par exemple:

log_and_run() { 
    local log_cmd="" 
    for arg in "[email protected]"; do 
     if [[ "$arg" == *" "* || -z "$arg" ]]; then 
      log_cmd="$log_cmd \"$arg\"" 
     else 
      log_cmd="$log_cmd $arg" 
     fi 
    done 
    echo "$(date): running command:$log_cmd" 
    ... 

Notez que cela gérer les espaces dans les paramètres et les paramètres vides, mais pas (par exemple) à l'intérieur des guillemets doubles paramètres. Faire ce "droit" peut se compliquer arbitrairement ...

Aussi, la façon dont je construis la chaîne log_cmd, il se termine avec un espace de début; J'ai traité ceci ci-dessus en omettant l'espace avant lui dans la commande d'écho. Si vous avez besoin de réduire l'espace, utilisez "${log_cmd# }".

+0

Merci, cela résout mon problème! Le coup de génie consistait à faire de la commande 'mock' complète un tableau. Cela a conduit à refactoriser ma fonction 'log_and_run()' qui utilisait naïvement '$ 1' pour enregistrer et exécuter la commande au lieu de' $ * 'et/ou' $ @ '. Je n'ai pas pu obtenir les guillemets autour de '' debug_package% {nil} "' pour apparaître dans les logs, mais c'est un détail mineur avec lequel je peux vivre. –

4

Les scripts shell peuvent être misérables. Dans de nombreux cas,diffère de . Vous pouvez essayer eval $cmd au lieu de $ cmd.

Essayez d'appeler ce script perl au lieu de la maquette:

#!/usr/bin/perl 
print join("\n",@ARGV),"\n"; 

Vous pourriez être surpris de voir ce que la liste des arguments est en fait:

-r 
myconfig 
--define 
"debug_package 
%{nil}" 
--resultdir 
results 
--rebuild 
mypackage.src.rpm 

Bien que je suppose que la sortie "set -x" essaie de vous montrer cela avec les guillemets simples.

Un truc sympa J'ai récemment appris est que

function f { 
    echo "[email protected]" 
} 

En fait correctement les arguments transmet de position à f ($* ne fonctionne pas).

Il ressemble à "eval" donne ce que vous avez l'intention probablement:

set -x # for debugging 
RESULTDIR=results 
MOCK_CONFIG="myconfig" 
MOCK_ARGS="-r $MOCK_CONFIG "'--define "debug_package %{nil}" --resultdir '"$RESULTDIR" 
cmd="mock $MOCK_ARGS --rebuild mypackage.src.rpm" 
echo $cmd 
eval $cmd 

Sortie:

+ echo mock -r myconfig --define '"debug_package' '%{nil}"' --resultdir results --rebuild mypackage.src.rpm 
mock -r myconfig --define "debug_package %{nil}" --resultdir results --rebuild mypackage.src.rpm 
+ eval mock -r myconfig --define '"debug_package' '%{nil}"' --resultdir results --rebuild mypackage.src.rpm 
++ mock -r myconfig --define 'debug_package %{nil}' --resultdir results --rebuild mypackage.src.rpm 
-r 
myconfig 
--define 
debug_package %{nil} 
--resultdir 
results 
--rebuild 
mypackage.src.rpm 
+2

Merci - eval était la solution rapide pour moi. –

Questions connexes