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?
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
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
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