2015-07-27 2 views
1

J'ai un utilitaire (myutil) que j'ai besoin de passer plusieurs paramètres. Les paramètres peuvent (contiendront) des barres obliques inverses et des espaces et doivent donc être placés entre guillemets simples. Un exemple de lancer cet utilitaire est:Passer plusieurs arguments entre guillemets pour commander dans bourne shell

myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 

Je travaille sur un script qui va lire ces paramètres à partir d'un fichier sur plusieurs lignes et les intégrer dans le script. Le fichier d'entrée myinput.txt ressemble à ceci:

# cat myinput.txt 
ONE\APPLE 
ONE\PEAR 
TWO\RED GRAPE 
TWO\TOMATO 

J'utilise le code suivant pour analyser le fichier et exécuter myutil. Le script lit chaque ligne comme un argument, et il enferme dans des guillemets doubles (comme myutil attendront des valeurs avec des espaces ou des caractères spéciaux) et crée une seule variable pour maintenir la chaîne entière de l'argument:

#!/bin/sh 
MYCOMMAND=myutil 
if [ -f myinput.txt ]; then 
    ARGSLIST=`cat myinput.txt| sed -e 's/^/"/g' -e 's/$/"/g' | awk '{ printf "%s ", $0 }'` 
    $MYCOMMAND setOptions "${ARGSLIST}" 
    printf "%s\n" "$MYCOMMAND setOptions ${ARGSLIST}" 
fi 

Comme je pourrais attendre, la sortie de l'écran de cette commande semble que prévu:

myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 

Cependant, ce n'est pas en fait ce qui est en cours d'exécution basé sur la façon dont myutil traiterait normalement cette commande. Au lieu de cela, myutil traite ceci comme si TOUS les arguments étaient également placés entre guillemets simples. L'exécution de cette mise au point sous sh ne reflète pas:

+ MYCOMMAND=myutil 
+ [ -f myinput.txt ] 
+ awk { printf "%s ", $0 } 
+ sed -e s/^/"/g -e s/$/"/g 
+ cat myinput.txt 
+ ARGSLIST="ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 
+ myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 
+ printf %s\n myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 
myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 

CEPENDANT, debug fonctionnant sous bash semble montrer ce qui est réellement en cours d'exécution:

+ MYCOMMAND=myutil 
+ '[' -f myinput.txt ']' 
++ awk '{ printf "%s ", $0 }' 
++ sed -e 's/^/"/g' -e 's/$/"/g' 
++ cat myinput.txt 
+ ARGSLIST='"ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" ' 
+ myutil setOptions '"ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" ' 
+ printf '%s\n' 'myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" ' 
myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 

Vous pouvez voir la ligne de débogage qui montre que:

+ myutil setOptions '"ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" ' 

Cette ligne est en fait ce qui est en cours d'exécution en fonction de la sortie que je vois dans mon util, mais ce qui est imprimé à l'écran est le AFORE mentionné moins les guillemets simples:

myutil setOptions "ONE\APPLE" "ONE\PEAR" "TWO\RED GRAPE" "TWO\TOMATO" 

J'ai besoin ce qui est affiché à l'écran pour être ce qui est réellement exécuté, et non pas la version avec les guillemets simples comme qui fournit un paramètre géant pour myutil qui est sans valeur.

J'ai deux solutions, dont je ne suis pas sûr que c'est la meilleure façon de gérer cela.

La première consiste à utiliser simplement eval:

#!/bin/sh 
MYCOMMAND=myutil 
if [ -f myinput.txt ]; then 
    ARGSLIST=`cat myinput.txt| sed -e 's/^/"/g' -e 's/$/"/g' | awk '{ printf "%s ", $0 }'` 
    eval $MYCOMMAND setOptions "${ARGSLIST}" 
    printf "%s\n" "$MYCOMMAND setOptions ${ARGSLIST}" 
fi 

La seconde est d'utiliser xargs:

#!/bin/sh 
MYCOMMAND=myutil 
if [ -f myinput.txt ]; then 
    ARGSLIST=`cat myinput.txt| sed -e 's/^/"/g' -e 's/$/"/g' | awk '{ printf "%s ", $0 }'` 
    printf "%s\n" "$ARGSLIST" | xargs $MYCOMMAND setOptions 
    printf "%s\n" "$MYCOMMAND setOptions ${ARGSLIST}" 
fi 

Je vais avoir du mal à croire que xargs est nécessaire , et j'ai lu à plusieurs reprises comment utiliser eval n'est probablement pas non plus un choix recommandé.

J'ai vu des sujets similaires à ce sujet avec des conseils pour utiliser des tableaux, mais je ne pouvais pas appliquer leurs conseils correctement à mon exemple.En outre, j'ai besoin de cette solution pour fonctionner sous bourde sh et être aussi portable que possible car il fonctionnera sur la plupart des saveurs standard de Linux/UNIX. Je ne crois pas sh prend en charge des tableaux dans le type que d'autres solutions ont recommandé.

Quelle est la meilleure méthode pour exécuter ma commande lorsqu'elle est affichée à l'écran sans que le devis ne soit fourni par le shell?

Répondre

0

Vous pouvez définir une fonction de shell récursive qui lit une ligne à partir de son entrée standard à la fois pour créer la ligne de commande. Quand aucune autre entrée n'est disponible, la commande actuelle est exécutée avec les arguments accumulés. La récursivité ajoute un peu de surcharge, mais je suppose que le fichier d'entrée n'est pas très gros.

run_from_file() { 
    if IFS= read -r newarg; then 
     run_from_file "[email protected]" "$newarg" 
    else 
     "$MYCOMMAND" "[email protected]" 
    fi 
} 

run_from_file < myinput.txt 
+0

Cela fonctionne sur la plupart des systèmes. Malheureusement, 'while read' sous' sh'on Solaris ne supporte pas le paramètre '-r'. Sans cela, je suis incapable d'analyser le fichier contenant les séparateurs '" \ "'. Bien que je puisse implémenter ceci dans une solution où je stocke les données avec un autre séparateur (disons '||') et que je traduise ensuite une fois que j'ai lu la ligne, je pense qu'il devrait peut-être y avoir une façon plus gracieuse? Je sais que les limites ici sont strictes ... pourquoi le 'sh' n'est pas compatible avec POSIX Je n'en ai aucune idée ... Mais merci pour l'exemple du tableau, ça fait d'autres exemples que j'ai vu beaucoup plus clairement. – BenH

+0

Je pense que vous feriez mieux de vous assurer que Solaris exécute le script avec '/ usr/xpg4/bin/sh' (un shell POSIX) plutôt que'/bin/sh' (qui est le * véritable * shell Bourne, à partir de Je rassemble). – chepner

+0

cela fonctionnerait, mais encore une fois je pourrais juste les faire utiliser '/ bin/bash' sur ces systèmes. On m'a donné la condition que "bash peut ne pas être disponible sur tous les systèmes" et donc, le script est écrit pour s'exécuter comme '#!/Bin/sh'. Je ne sais pas comment nous pourrions nous assurer que le script fonctionne avec le shell approprié. Si nous avons placé '#!/Usr/xpg4/bin/sh' en haut, alors tout système n'ayant pas ce chemin (non Solaris) échouera avec' mauvais interpréteur'. Je pense que la seule façon de choisir la bonne serait d'avoir un script wrapper, que nous voudrions éviter. – BenH