Comment urlencode les données pour la commande curl?

J’essaie d’écrire un script bash pour tester qui prend un paramètre et l’envoie via curl au site Web. Je dois encoder la valeur pour que les caractères spéciaux soient traités correctement. Quelle est la meilleure façon de procéder?

Voici mon script de base jusqu’à présent:

#!/bin/bash host=${1:?'bad host'} value=$2 shift shift curl -v -d "param=${value}" http://${host}/somepath $@ 

Utilisez curl --data-urlencode ; de l’ man curl :

Ceci publie des données, similaires aux autres options --data à l’exception du fait que cela effectue un encodage d’URL. Pour être compatible avec CGI, la partie doit commencer par un nom suivi d’un séparateur et d’une spécification de contenu.

Exemple d’utilisation:

 curl \ --data-urlencode "paramName=value" \ --data-urlencode "secondParam=value" \ http://example.com 

Voir la page de manuel pour plus d’informations.

Cela nécessite curl 7.18.0 ou plus récent (publié en janvier 2008) . Utilisez curl -V pour vérifier quelle version vous avez.

Voici la réponse BASH pure.

 rawurlencode() { local ssortingng="${1}" local strlen=${#ssortingng} local encoded="" local pos co for (( pos=0 ; pos 

Vous pouvez l'utiliser de deux manières:

 easier: echo http://url/q?=$( rawurlencode "$args" ) faster: rawurlencode "$args"; echo http://url/q?${REPLY} 

[édité]

Voici la fonction rawurldecode () correspondante, qui - avec toute la modestie - est géniale.

 # Returns a ssortingng in which the sequences with percent (%) signs followed by # two hex digits have been replaced with literal characters. rawurldecode() { # This is perhaps a risky gambit, but since all escape characters must be # encoded, we can replace %NN with \xNN and pass the lot to printf -b, which # will decode hex for us printf -v REPLY '%b' "${1//%/\\x}" # You can either set a return variable (FASTER) echo "${REPLY}" #+or echo the result (EASIER)... or both... :p } 

Avec l'ensemble correspondant, nous pouvons maintenant effectuer des tests simples:

 $ diff rawurlencode.inc.sh \ < ( rawurldecode "$( rawurlencode "$( cat rawurlencode.inc.sh )" )" ) \ && echo Matched Output: Matched 

Et si vous sentez vraiment que vous avez besoin d'un outil externe (bon, ça ira beaucoup plus vite, et peut faire des fichiers binarys et autres ...) Je l'ai trouvé sur mon routeur OpenWRT ...

 replace_value=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed) 

Où url_escape.sed était un fichier contenant ces règles:

 # sed url escaping s:%:%25:g s: :%20:g s:<: %3C:g s:>:%3E:g s:#:%23:g s:{:%7B:g s:}:%7D:g s:|:%7C:g s:\\:%5C:g s:\^:%5E:g s:~:%7E:g s:\[:%5B:g s:\]:%5D:g s:`:%60:g s:;:%3B:g s:/:%2F:g s:?:%3F:g s^:^%3A^g s:@:%40:g s:=:%3D:g s:&:%26:g s:\$:%24:g s:\!:%21:g s:\*:%2A:g 

Utilisez le module URI::Escape de Perl et la fonction uri_escape dans la deuxième ligne de votre script bash:

 ... value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")" ... 

Edit: Corrigez les problèmes de cotation, comme suggéré par Chris Johnsen dans les commentaires. Merci!

par souci d’exhaustivité, de nombreuses solutions utilisant sed ou awk ne traduisent qu’un ensemble spécial de caractères et sont donc assez volumineuses en termes de taille de code et ne traduisent pas non plus d’autres caractères spéciaux à coder.

un moyen sûr d’urlencode serait de coder chaque octet – même ceux qui auraient été autorisés.

 echo -ne 'some random\nbytes' | xxd -plain | tr -d '\n' | sed 's/\(..\)/%\1/g' 

xxd prend soin ici que l’entrée soit traitée comme des octets et non des caractères.

modifier:

xxd est fourni avec le paquetage vim-common dans Debian et j’étais juste sur un système où il n’était pas installé et je ne voulais pas l’installer. L’alternative est d’utiliser hexdump du paquet bsdmainutils dans Debian. Selon le graphique ci-dessous, bsdmainutils et vim-common devraient avoir la même probabilité d’être installés:

http://qa.debian.org/popcon-png.php?packages=vim-common%2Cbsdmainutils&show_installed=1&want_legend=1&want_ticks=1

mais néanmoins voici une version qui utilise hexdump au lieu de xxd et permet d’éviter l’appel tr :

 echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g' 

Je le trouve plus lisible en python:

 encoded_value=$(python -c "import urllib; print urllib.quote('''$value''')") 

le sortingple ‘garantit que les guillemets simples en valeur ne feront pas de mal. urllib est dans la bibliothèque standard. Cela fonctionne pour examiner pour cette url (monde réel) fou:

 "http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7 

Une des variantes, peut-être moche, mais simple:

 urlencode() { local data if [[ $# != 1 ]]; then echo "Usage: $0 ssortingng-to-urlencode" return 1 fi data="$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "$1" "")" if [[ $? != 3 ]]; then echo "Unexpected error" 1>&2 return 2 fi echo "${data##/?}" return 0 } 

Voici la version à un trait par exemple (comme suggéré par Bruno ):

 date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3- 

J’ai trouvé l’extrait de code suivant utile pour le coller dans une chaîne d’appels de programme, où URI :: Escape peut ne pas être installé:

 perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg' 

( source )

Si vous souhaitez lancer une requête GET et utiliser un curl pur, ajoutez simplement --get à la solution de @ Jacob.

Voici un exemple:

 curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed 

Une autre option consiste à utiliser jq :

 jq -s -R -r @uri 

-s ( --slurp ) lit les lignes d’entrée dans un tableau et -s -R ( --slurp --raw-input ) lit l’entrée dans une chaîne unique. -r ( --raw-output ) --raw-output le contenu des chaînes au lieu des chaînes de caractères JSON.

Ou ce pourcentage-encode tous les octets:

 xxd -p|tr -d \\n|sed 's/../%&/g' 

Lien direct vers la version awk: http://www.shelldorado.com/scripts/cmds/urlencode
Je l’ai utilisé pendant des années et cela fonctionne comme un charme

 : ########################################################################## # Title : urlencode - encode URL data # Author : Heiner Steven (heiner.steven@odn.de) # Date : 2000-03-15 # Requires : awk # Categories : File Conversion, WWW, CGI # SCCS-Id. : @(#) urlencode 1.4 06/10/29 ########################################################################## # Description # Encode data according to # RFC 1738: "Uniform Resource Locators (URL)" and # RFC 1866: "Hypertext Markup Language - 2.0" (HTML) # # This encoding is used ie for the MIME type # "application/x-www-form-urlencoded" # # Notes # o The default behaviour is not to encode the line endings. This # may not be what was intended, because the result will be # multiple lines of output (which cannot be used in an URL or a # HTTP "POST" request). If the desired output should be one # line, use the "-l" option. # # o The "-l" option assumes, that the end-of-line is denoted by # the character LF (ASCII 10). This is not true for Windows or # Mac systems, where the end of a line is denoted by the two # characters CR LF (ASCII 13 10). # We use this for symmetry; data processed in the following way: # cat | urlencode -l | urldecode -l # should (and will) result in the original data # # o Large lines (or binary files) will break many AWK # implementations. If you get the message # awk: record `...' too long # record number xxx # consider using GNU AWK (gawk). # # o urlencode will always terminate it's output with an EOL # character # # Thanks to Stefan Brozinski for pointing out a bug related to non-standard # locales. # # See also # urldecode ########################################################################## PN=`basename "$0"` # Program name VER='1.4' : ${AWK=awk} Usage () { echo >&2 "$PN - encode URL data, $VER usage: $PN [-l] [file ...] -l: encode line endings (result will be one line of output) The default is to encode each input line on its own." exit 1 } Msg () { for MsgLine do echo "$PN: $MsgLine" >&2 done } Fatal () { Msg "$@"; exit 1; } set -- `getopt hl "$@" 2>/dev/null` || Usage [ $# -lt 1 ] && Usage # "getopt" detected an error EncodeEOL=no while [ $# -gt 0 ] do case "$1" in -l) EncodeEOL=yes;; --) shift; break;; -h) Usage;; -*) Usage;; *) break;; # First file name esac shift done LANG=C export LANG $AWK ' BEGIN { # We assume an awk implementation that is just plain dumb. # We will convert an character to its ASCII value with the # table ord[], and produce two-digit hexadecimal output # without the printf("%02X") feature. EOL = "%0A" # "end of line" ssortingng (encoded) split ("1 2 3 4 5 6 7 8 9 ABCDEF", hextab, " ") hextab [0] = 0 for ( i=1; i< =255; ++i ) ord [ sprintf ("%c", i) "" ] = i + 0 if ("'"$EncodeEOL"'" == "yes") EncodeEOL = 1; else EncodeEOL = 0 } { encoded = "" for ( i=1; i<=length ($0); ++i ) { c = substr ($0, i, 1) if ( c ~ /[a-zA-Z0-9.-]/ ) { encoded = encoded c # safe character } else if ( c == " " ) { encoded = encoded "+" # special handling } else { # unsafe character, encode it as a two-digit hex-number lo = ord [c] % 16 hi = int (ord [c] / 16); encoded = encoded "%" hextab [hi] hextab [lo] } } if ( EncodeEOL ) { printf ("%s", encoded EOL) } else { print encoded } } END { #if ( EncodeEOL ) print "" } ' "$@" 

C’est peut-être le meilleur:

 after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s") 
 url=$(echo "$1" | sed -e 's/%/%25/g' -e 's/ /%20/g' -e 's/!/%21/g' -e 's/"/%22/g' -e 's/#/%23/g' -e 's/\$/%24/g' -e 's/\&/%26/g' -e 's/'\''/%27/g' -e 's/(/%28/g' -e 's/)/%29/g' -e 's/\*/%2a/g' -e 's/+/%2b/g' -e 's/,/%2c/g' -e 's/-/%2d/g' -e 's/\./%2e/g' -e 's/\//%2f/g' -e 's/:/%3a/g' -e 's/;/%3b/g' -e 's//%3e/g' -e 's/?/%3f/g' -e 's/@/%40/g' -e 's/\[/%5b/g' -e 's/\\/%5c/g' -e 's/\]/%5d/g' -e 's/\^/%5e/g' -e 's/_/%5f/g' -e 's/`/%60/g' -e 's/{/%7b/g' -e 's/|/%7c/g' -e 's/}/%7d/g' -e 's/~/%7e/g') 

Cela va encoder la chaîne à l’intérieur de $ 1 et la sortir dans $ url. bien que vous ne deviez pas le mettre dans un var si vous voulez. BTW n’a pas inclus le sed pour l’onglet pensait qu’il le transformerait en espaces

Pour ceux d’entre vous qui recherchent une solution qui n’a pas besoin de perl, en voici une qui ne nécessite que hexdump et awk:

 url_encode() { [ $# -lt 1 ] && { return; } encodedurl="$1"; # make sure hexdump exists, if not, just give back the url [ ! -x "/usr/bin/hexdump" ] && { return; } encodedurl=` echo $encodedurl | hexdump -v -e '1/1 "%02x\t"' -e '1/1 "%_c\n"' | LANG=C awk ' $1 == "20" { printf("%s", "+"); next } # space becomes plus $1 ~ /0[adAD]/ { next } # ssortingp newlines $2 ~ /^[a-zA-Z0-9.*()\/-]$/ { printf("%s", $2); next } # pass through what we can { printf("%%%s", $1) } # take hex value of everything else '` } 

Cousu ensemble à partir de deux endroits à travers le filet et quelques essais et erreurs locaux. Ça marche super bien!

Utiliser php depuis un script shell:

 value="http://www.google.com" encoded=$(php -r "echo rawurlencode('$value');") # encoded = "http%3A%2F%2Fwww.google.com" echo $(php -r "echo rawurldecode('$encoded');") # returns: "http://www.google.com" 
  1. http://www.php.net/manual/en/function.rawurlencode.php
  2. http://www.php.net/manual/en/function.rawurldecode.php

uni2ascii est très pratique:

 $ echo -ne '你好世界' | uni2ascii -aJ %E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C 

Si vous ne voulez pas dépendre de Perl, vous pouvez également utiliser sed. C’est un peu désordonné, car chaque personnage doit être échappé individuellement. Créez un fichier avec le contenu suivant et appelez-le urlencode.sed

 s/%/%25/g s/ /%20/g s/ /%09/g s/!/%21/g s/"/%22/g s/#/%23/g s/\$/%24/g s/\&/%26/g s/'\''/%27/g s/(/%28/g s/)/%29/g s/\*/%2a/g s/+/%2b/g s/,/%2c/g s/-/%2d/g s/\./%2e/g s/\//%2f/g s/:/%3a/g s/;/%3b/g s//%3e/g s/?/%3f/g s/@/%40/g s/\[/%5b/g s/\\/%5c/g s/\]/%5d/g s/\^/%5e/g s/_/%5f/g s/`/%60/g s/{/%7b/g s/|/%7c/g s/}/%7d/g s/~/%7e/g s/ /%09/g 

Pour l’utiliser, procédez comme suit.

 STR1=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f1) STR2=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f2) OUT2=$(echo "$STR2" | sed -f urlencode.sed) echo "$STR1?$OUT2" 

Cela divisera la chaîne en une partie nécessitant un encodage, et la partie qui est correcte, encodera la partie qui en a besoin, puis se recoupera.

Vous pouvez mettre cela dans un script sh pour plus de commodité, peut-être demander à un paramètre de l’encoder, le mettre sur votre chemin et vous pouvez simplement appeler:

 urlencode https://www.exxample.com?isThisFun=HellNo 

la source

La question est de faire cela en bash et il n’y a pas besoin de python ou de perl car il y a en fait une seule commande qui fait exactement ce que vous voulez – “urlencode”.

 value=$(urlencode "${2}") 

C’est aussi bien mieux, car la réponse ci-dessus à Perl, par exemple, ne code pas correctement tous les caractères. Essayez-le avec le long tiret que vous obtenez de Word et vous obtenez le mauvais encodage.

Notez que vous devez installer “gridsite-clients” pour fournir cette commande.

Vous pouvez émuler l’ encodeURIComponent de javascript dans perl. Voici la commande:

 perl -pe 's/([^a-zA-Z0-9_.!~*()'\''-])/sprintf("%%%02X", ord($1))/ge' 

Vous pouvez définir cela comme un alias bash dans .bash_profile :

 alias encodeURIComponent='perl -pe '\''s/([^a-zA-Z0-9_.!~*()'\''\'\'''\''-])/sprintf("%%%02X",ord($1))/ge'\' 

Vous pouvez maintenant encodeURIComponent dans encodeURIComponent :

 $ echo -n 'hèllo wôrld!' | encodeURIComponent h%C3%A8llo%20w%C3%B4rld! 

Option PHP simple:

 echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);' 

Une autre approche php:

 echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));" 

Voici la version du noeud:

 uriencode() { node -p "encodeURIComponent('${1//\'/\\\'}')" } 

Ruby, pour être complet

 value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")" 

Voici une solution Bash qui n’invoque aucun programme externe:

 uriencode() { s="${1//'%'/%25}" s="${s//' '/%20}" s="${s//'"'/%22}" s="${s//'#'/%23}" s="${s//'$'/%24}" s="${s//'&'/%26}" s="${s//'+'/%2B}" s="${s//','/%2C}" s="${s//'/'/%2F}" s="${s//':'/%3A}" s="${s//';'/%3B}" s="${s//'='/%3D}" s="${s//'?'/%3F}" s="${s//'@'/%40}" s="${s//'['/%5B}" s="${s//']'/%5D}" printf %s "$s" } 

Voici une fonction POSIX pour faire cela:

 encodeURIComponent() { awk 'BEGIN {while (y++ < 125) z[sprintf("%c", y)] = y while (y = substr(ARGV[1], ++j, 1)) q = y ~ /[[:alnum:]_.!~*\47()-]/ ? qy : q sprintf("%%%02X", z[y]) print q}' "$1" } 

Exemple:

 value=$(encodeURIComponent "$2") 

La source

Voici une conversion à une ligne utilisant Lua, similaire à la réponse de blueyed, à l’ exception de tous les caractères non réservés RFC 3986 non codés (comme cette réponse ):

 url=$(echo 'print((arg[1]:gsub("([^%w%-%.%_%~])",function(c)return("%%%02X"):format(c:byte())end)))' | lua - "$1") 

De plus, vous devrez vous assurer que les nouvelles lignes de votre chaîne sont converties de LF à CRLF, auquel cas vous pouvez insérer un gsub("\r?\n", "\r\n") dans la chaîne avant le pourcentage. codage.

Voici une variante qui, dans le style non standard d’application / x-www-form-urlencoded , fait que la nouvelle normalisation, ainsi que le codage des espaces, est remplacé par «+» au lieu de «% 20» (qui pourrait probablement être ajouté au Extrait Perl utilisant une technique similaire).

 url=$(echo 'print((arg[1]:gsub("\r?\n", "\r\n"):gsub("([^%w%-%.%_%~ ]))",function(c)return("%%%02X"):format(c:byte())end):gsub(" ","+"))' | lua - "$1") 

Après avoir installé php, j’utilise cette méthode:

 URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"` 

Ceci est la version ksh de la réponse orwellophile contenant les fonctions rawurlencode et rawurldecode (link: Comment urlencode des données pour la commande curl? ). Je n’ai pas assez de rep pour poster un commentaire, d’où le nouveau post ..

 #!/bin/ksh93 function rawurlencode { typeset ssortingng="${1}" typeset strlen=${#ssortingng} typeset encoded="" for (( pos=0 ; pos C%2b%2b print $(rawurldecode "C%2b%2b") # --> C++ 

Voici ma version pour le shell cendré de busybox pour un système embarqué, j’ai à l’origine adopté la variante d’Orwellophile:

 urlencode() { local S="${1}" local encoded="" local ch local o for i in $(seq 0 $((${#S} - 1)) ) do ch=${S:$i:1} case "${ch}" in [-_.~a-zA-Z0-9]) o="${ch}" ;; *) o=$(printf '%%%02x' "'$ch") ;; esac encoded="${encoded}${o}" done echo ${encoded} } urldecode() { # urldecode  local url_encoded="${1//+/ }" printf '%b' "${url_encoded//%/\\x}" } 

Ce qui suit est basé sur la réponse d’Orwellophile, mais résout le bug multi-octets mentionné dans les commentaires en définissant LC_ALL = C (une astuce de vte.sh). Je l’ai écrit sous la forme d’une fonction appropriée PROMPT_COMMAND, parce que c’est comme ça que je l’utilise.

 print_path_url() { local LC_ALL=C local ssortingng="$PWD" local strlen=${#ssortingng} local encoded="" local pos co for (( pos=0 ; pos 

Qu’est-ce qui parsingrait les URL mieux que javascript?

 node -p "encodeURIComponent('$url')"