Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

shell : repasser les arguments d'un script en variable POST via curl

18 réponses
Avatar
Francois Lafont
Bonsoir à tous,

Je suis confronté à un problème de script shell dont
je pensais de prime abord que j'allais en venir à
bout relativement facilement mais finalement je coince
complètement... peut-être parce que je suis passé à
côté d'un truc simple. Je ne sais pas.

J'ai un script qui reçoit des arguments en ligne de
commandes. Par exemple :

./mon-script "arg1" "aaa aa" "bbbb"

Le nombre d'arguments n'est pas constant à chaque appel.
C'est toujours au moins 1 mais à part ça on ne rien
supposer (des fois ce sera 3, d'autres fois 10 etc).

Je voudrais que le script lance un curl sur une page
en localhost en passant ses arguments en variables
POST comme ceci :

curl -d "token1=arg1" -d "token2=aaa aa" -d "token3=bbbb" http://localhost/ma-page
# la variable POST token1 contient le premier paramètre du script.
# la variable POST token2 contient le deuxième paramètre du script.
# etc.

Si par exemple le script était appelé avec systématiquement
3 arguments, alors je pense qu'il me suffirait de coder le
script comme ceci :

-------------------------------------------------------
#!/bin/sh

curl -d "token1=$1" -d "token2=$2" -d "token3=$3" http://localhost/ma-page
-------------------------------------------------------

Seulement, comme je l'ai indiqué plus haut, le nombre d'arguments
n'est pas forcément constant. Il y a donc une sorte de boucle
à mettre en place ici mais là je coince complètement. Au départ,
j'ai pensé à ça :

-------------------------------------------------------
#!/bin/sh

i=1
for arg in "$@"
do
options="$options -d token$i=$arg"
i=$((i+1))
done

curl $options http://localhost/ma-page
-------------------------------------------------------

Mais le script se vautre lamentablement dès qu'un argument
possède un espace. Dans le cas de mon exemple précédent, c'est
le curl suivant qui serait appelé :

curl -d token1=arg1 -d token2=aaa aa -d token3=bbbb http://localhost/ma-page
^^^^^^^^

Or, les paramètres peuvent potentiellement contenir des
espaces (et d'autres caractères « exotiques »). Je souhaiterais
pouvoir faire cela avec le shell sh de ma Debian Wheezy.
Est-ce que vous voyez un moyen de le faire ?

Merci d'avance pour vos éventuelles pistes.

--
François Lafont

10 réponses

1 2
Avatar
ST
On 2014-07-05, Francois Lafont wrote:

-------------------------------------------------------
#!/bin/sh

i=1
for arg in "$@"
do
options="$options -d token$i=$arg"
i=$((i+1))
done

curl $options http://localhost/ma-page
-------------------------------------------------------

Mais le script se vautre lamentablement dès qu'un argument
possède un espace. Dans le cas de mon exemple précédent, c'est
le curl suivant qui serait appelé :

curl -d token1=arg1 -d token2ªa aa -d token3»bb http://localhost/ma-page



Ton script ne se plante pas, il fait même très exactement ce que tu lui
demandes. Si un argument a des espaces, il est rajouté avec des espaces.

Par contre, tel que tu l'écris, curl est incapable de l'interpréter
correctement (je trouve pas l'option -d dans le man de curl).

------------------------------------------------
#!/bin/sh

i=1
for arg in "$@"
do
options="$options -d token$i="$arg""
i=$((i+1))
done

echo $options
------------------------------------------------

Place les arguments entre ", ce qui devrait permettre de les interpréter
de façon correcte.


--
Notre race est la race des maîtres. Nous sommes des dieux vivants sur
cette planète. Nous sommes aussi différents des races inférieures comme
ils le sont des insectes. Notre destin doit être de régner sur ces
races. Menahem Begin 1er Ministre d'Israel 1977-1983.
Avatar
Francois Lafont
Le 05/07/2014 06:22, ST a écrit :

Ton script ne se plante pas, il fait même très exactement ce que tu lui
demandes.



C'est un point de vue. ;)

Si un argument a des espaces, il est rajouté avec des espaces.

Par contre, tel que tu l'écris, curl est incapable de l'interpréter
correctement



Je ne vois pas tout à fait le chose de cette manière.
Pour moi, curl n'est pas le problème. Ça pourrait être
n'importe quelle commande à la place, le problème serait
le même.

(je trouve pas l'option -d dans le man de curl).



Sur ma Wheezy :

apt-get install curl
man curl

Et là j'ai :

-d, --data <data>
(HTTP) Sends the specified data in a POST
request to the HTTP server [...]

Mais on est d'accord qu'ici curl n'est pas le problème.
Mon problème est un problème purement shell.

------------------------------------------------
#!/bin/sh

i=1
for arg in "$@"
do
options="$options -d token$i="$arg""
i=$((i+1))
done

echo $options
------------------------------------------------

Place les arguments entre ", ce qui devrait permettre de les interpréter
de façon correcte.



J'avais pensé à faire cela mais cela ne fonctionne pas.
Les « " » vont tout simplement faire partie des variables
POST et le problème des espaces ne sera pas résolu. On
peut le montrer en remplaçant curl par une fonction qui
se contente d'afficher les arguments qu'elle reçoit.

------------------------------------------------
#!/bin/sh

# Fonction qui liste les arguments qu'elle reçoit.
f () {
j=1
for k in "$@"
do
printf "arg%s --> [%s]n" "$j" "$k"
j=$((j+1))
done
}

i=1
for arg in "$@"
do
options="$options -d token$i="$arg""
i=$((i+1))
done

f $options

printf "nListe correctes des arguments dans le cas où le nb d'arguments au départ est 3 :nn"

f -d "token1=$1" -d "token2=$2" -d "token3=$3"
------------------------------------------------

Voici ce que donne un appel :

~$ ./test.sh "arg1" "aaa aa" "bbbb"
arg1 --> [-d]
arg2 --> [token1="arg1"]
arg3 --> [-d]
arg4 --> [token2="aaa]
arg5 --> [aa"]
arg6 --> [-d]
arg7 --> [token3="bbbb"]

Liste correctes des arguments dans le cas où le nb d'arguments au départ est 3 :

arg1 --> [-d]
arg2 --> [token1=arg1]
arg3 --> [-d]
arg4 --> [token2ªa aa]
arg5 --> [-d]

On voit vient que, dans le premier cas, une des
variables POST envoyées sera « "arg1" » et pas
« arg1 ». On voit également que le problème des
espaces demeure car la commande curl recevrait
un paramètre « aa" » tout seul.

En gros, au départ, j'ai une liste de tokens (dans
la page man de bash, ils appellent cela des "mots")
"$@" qui correspond très fidèlement aux arguments
que reçoit mon script. Mon problème c'est que je veux
modifier cette liste de tokens pour y intercaler les
tokens « -d » et « tokenN=... » où ... est le Nième
token de "$@" et faire passer tout cela à curl (mais
ça pourrait être une autre commande que ça serait
pareil). Or, j'ai l'impression que c'est impossible
à faire sans altérer la liste des tokens que le script
a reçu initialement.

--
François Lafont
Avatar
Nicolas George
Francois Lafont , dans le message
<53b74c6d$0$2037$, a écrit :
Mais le script se vautre lamentablement dès qu'un argument
possède un espace.



Le shell a du mal avec les chaînes de caractères contenant des caractères
spéciaux, il faut être soigneux. Si on veut gérer une liste de telles
chaînes, comme c'est ton cas, ça devient très difficile avec du sh normal.

Utilise un langage de script décent.
Avatar
Francois Lafont
Le 05/07/2014 15:26, Nicolas George a écrit :

Le shell a du mal avec les chaînes de caractères contenant des caractères
spéciaux, il faut être soigneux. Si on veut gérer une liste de telles
chaînes, comme c'est ton cas, ça devient très difficile avec du sh normal.



Oui, je le vois bien avec l'exemple exposé dans ce fil.
C'est difficile sans doute mais pas impossible. Je pense
que je peux m'en sortir avec du eval par exemple, mais
c'est très moche.

Utilise un langage de script décent.



Et oui... dans l'absolu tu as raison. On voit bien que le
shell n'est pas adapté à mon problème et qu'avec un petit
script en Perl (par exemple), je pourrais faire ce que je
veux en 2 coups de cuillère à pot[*]. Mais le contexte fait
que j'aimerais bien pouvoir faire du shell. En effet, le
script dans mon cas est un simple lanceur :

1. Il est appelé avec des paramètres.
2. Il retransmet ces paramètres en variables POST via
un curl à destination d'une page http qui lance un
script Perl justement (et le script Perl utilisera
ces variables POST pour faire son travail).

[*] En fait, en relisant mon message avant de poster, je
m'aperçois que j'ai quand même un doute sur le fait que ça
soit si facile avec du Perl par exemple. Car l'objectif
est au final de lancer la commande curl avec les « bonnes »
options. Or, j'ai l'impression que je vais me retrouver
avec les mêmes problématiques qu'en shell, à savoir construire
une chaîne de caractères qui va correspondre à l'appel de
curl, tout en devant faire attention aux caractères spéciaux
etc.

En fait, ce fil est en relation avec un fil précédent (auquel
tu as participé) où j'expliquais que je voulais lancer des
checks (basés sur SNMP), que ces checks étaient lancés très
souvent et très régulièrement, d'où une certaine importance de
l'aspect performance. À cette occasion, tu m'avais expliqué,
avec raison, que contrairement à ce que je pensais le Perl
pouvait être plus rapide que le shell dans ce type de travail
*si* l'on ne tenait pas compte du temps de chargement de
l'interpréteur lui-même (si on comptait le temps de chargement
de l'interpréteur lui-même, alors le shell l'emportait
largement). Et effectivement, j'ai pu constater par exemple
que si je lançais des checks en Perl via Apache et le module
mod_perl, alors j'avais de très bonnes perf (Apache charge
une bonne fois pour toutes l'interpréteur Perl et ensuite
les checks sont très rapide s'exécuter). Du coup, je voudrais
mettre en place ce genre « système », à savoir des checks en
Perl exécutés par Apache via mod_perl. Seulement, il me faut
un lanceur qui déclenche les checks, ie un programme assez
trivial qui reçoit les arguments dont le check a besoin, fait
la requête http, via un curl, sur Apache vers la page qui
correspond au check en lui passant les variables nécessaires
en variables POST et récupère la sortie de la requête http
qui ne sera autre que le résultat du check.

Du coup, je vois les choses ainsi :

- un lanceur en shell très léger car au final ce lanceur
ne fait pas grand chose et le shell sh est vraiment
rapide à se charger.
- des checks en Perl qui sont très rapides au niveau de
leur exécution à condition que l'interpréteur Perl
soit déjà préchargé, ce qui se passe avec Apache et
mod_perl.

Si au final mon lanceur devient un script Perl (ou Python
ou un truc un peu plus évolué que le shell) alors ça casse
un peu toute l'idée générale qui est d'avoir un lanceur
« trivial » et léger qui ne fait que lancer des requêtes
http sur des scripts Perl qui eux font le travail.

--
François Lafont
Avatar
Francois Lafont
Le 06/07/2014 03:41, Francois Lafont a écrit :

Oui, je le vois bien avec l'exemple exposé dans ce fil.
C'est difficile sans doute mais pas impossible. Je pense
que je peux m'en sortir avec du eval par exemple, mais
c'est très moche.



Voici le code très moche qui a l'air de faire le travail
(il me semble) :

-------------------------------------
#!/bin/sh

export LC_ALL=C
export PATH="/usr/bin:/bin"

# Fonction qui liste les arguments qu'elle reçoit.
f () {
j=1
for k in "$@"
do
printf "arg%s --> [%s]n" "$j" "$k"
j=$((j+1))
done
}

replace_quote="'"'"'"

i=1
for arg in "$@"
do
arg=$(printf "%s" "$arg" | sed "s/'/$replace_quote/g")
options="$options -d 'token$i=$arg'"
i=$((i+1))
done

eval "f $options http://localhost/ma-page"
# eval "curl $option http://localhost/ma-page"
-------------------------------------

Il me semble qu'en Perl j'en serais réduit aux
même genre de contorsions mais je me trompe peut-être.
Je connais très mal le Perl pour l'instant.

--
François Lafont
Avatar
Nicolas George
Francois Lafont , dans le message
<53b8aafb$0$2213$, a écrit :
arg=$(printf "%s" "$arg" | sed "s/'/$replace_quote/g")



Et les guillemets doubles ? Et les dollars ? Et les backslashes ? Et les
backquotes ?

Il me semble qu'en Perl j'en serais réduit aux
même genre de contorsions mais je me trompe peut-être.



Tu te trompes. Perl, contrairement au shell, a des tableaux à part entière.
Et peut évidemment invoquer une commande avec sa liste d'arguments précise
sans invoquer un shell intermédiaire.

Si cette dernière phrase te semble le moins du monde obscure, c'est que tu
n'as pas encore assez bien compris le principe de lancement d'un processus
Unix pour pouvoir mener à bien ta tâche, et je t'invite à lire en détail la
page de man d'excve(2).
Avatar
Nicolas George
Francois Lafont , dans le message
<53b8a92b$0$2267$, a écrit :
1. Il est appelé avec des paramètres.
2. Il retransmet ces paramètres en variables POST via
un curl à destination d'une page http qui lance un
script Perl justement (et le script Perl utilisera
ces variables POST pour faire son travail).



Ces deux arguments abondent dans mon sens : il faut utiliser un vrai langage
de script.

Seulement, il me faut
un lanceur qui déclenche les checks, ie un programme assez
trivial qui reçoit les arguments dont le check a besoin



Dans ce cas, arrange-toi pour que les arguments en question soient formés
exclusivement de caractères alphanumériques, inertes pour le shell.

Et sinon, passe au C.
Avatar
Fred
On 05/07/2014 02:53, Francois Lafont wrote:
Bonsoir à tous,




salut

-------------------------------------------------------
#!/bin/sh

curl -d "token1=$1" -d "token2=$2" -d "token3=$3" http://localhost/ma-page
-------------------------------------------------------

Seulement, comme je l'ai indiqué plus haut, le nombre d'arguments
n'est pas forcément constant. Il y a donc une sorte de boucle
à mettre en place ici mais là je coince complètement. Au départ,
j'ai pensé à ça :




Tu peux aussi reprendre cette commande avec autant de token qu'il peut
y avoir d'arguments. Au pire, les derniers tokens seront vide mais
comme ta page doit analyser les arguments qu'elle reçoit, ce ne sera
pas un problème.


Fred
Avatar
Fred
On 05/07/2014 02:53, Francois Lafont wrote:


Mais le script se vautre lamentablement dès qu'un argument
possède un espace. Dans le cas de mon exemple précédent, c'est
le curl suivant qui serait appelé :

curl -d token1=arg1 -d token2ªa aa -d token3»bb http://localhost/ma-page
^^^^^^^^

Or, les paramètres peuvent potentiellement contenir des
espaces (et d'autres caractères « exotiques »). Je souhaiterais
pouvoir faire cela avec le shell sh de ma Debian Wheezy.
Est-ce que vous voyez un moyen de le faire ?




Il est possible de convertir les caractères spéciaux en unicode.

http://journalxtra.com/fr/linux/howto-pass-data-variables-to-curl-to-autofill-web-forms/

L'encodage est fait par des sed successif. Pas terrible niveau perfs.

Il serait mieux d'encoder les paramètres en amont par le script
appelant si il dispose d'une fonction adaptée.

Par contre, comme il n'y a pas de guillemets autour des token=,
le décodage n'est pas automatique (en php) et il faut faire
un urldecode() sur les paramètres.
Avatar
Nicolas George
Fred , dans le message <lpbcm5$5gt$, a écrit :
Il est possible de convertir les caractères spéciaux en unicode.



Tu es conscient que ça ne veut essentiellement rien dire ?

http://journalxtra.com/fr/linux/howto-pass-data-variables-to-curl-to-autofill-web-forms/



Beurk. Il ferait mieux de lire le man de cURL et d'utiliser -F.
1 2