web-dev-qa-db-fra.com

Comment urlencoder les données pour la commande curl?

J'essaie d'écrire un script bash à tester qui prend un paramètre et l'envoie via curl au site Web. Je dois encoder la valeur dans l'URL pour m'assurer que les caractères spéciaux sont 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 $@
286
Aaron

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

Ceci publie des données, similaires aux autres options --data, à l'exception que cela effectue un codage d'URL. Pour être conforme à CGI, la partie <data> devrait 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 votre version.

344
Jacob R

Voici la réponse pure de BASH.

rawurlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"    # You can either set a return variable (FASTER) 
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

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 - en toute modestie - est géniale.

# Returns a string 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 le jeu de correspondance, nous pouvons maintenant effectuer quelques 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 (enfin, il ira beaucoup plus vite, et pourrait faire des fichiers binaires, etc.), 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
161
Orwellophile

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

...

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

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

90
dubek

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

un moyen sûr d'urlencoder serait de simplement encoder 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 veille à ce que l'entrée soit gérée en octets et non en caractères.

modifier:

xxd est livré avec le paquet 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'option consiste à utiliser hexdump à partir du paquet bsdmainutils dans Debian. Selon le graphique suivant, bsdmainutils et vim-common devraient avoir une probabilité à peu près égale 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 à la place de xxd et permet d'éviter l'appel tr:

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

Une des variantes peut être laide, mais simple:

urlencode() {
    local data
    if [[ $# != 1 ]]; then
        echo "Usage: $0 string-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 à une ligne par exemple (comme suggéré par Bruno ):

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

# If you experience the trailing %0A, use
date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | sed -E 's/..(.*).../\1/'
49
Sergey

Je le trouve plus lisible en python:

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

le triple 'garantit que les guillemets simples en valeur ne feront pas mal. urllib est dans la bibliothèque standard. Cela fonctionne pour examiner pour cette URL folle (monde réel):

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

Une autre option consiste à utiliser jq:

jq -sRr @uri

-R (--raw-input) traite les lignes d'entrée en tant que chaînes au lieu de les analyser en tant que JSON et -sR (--Slurp --raw-input) lit l'entrée en une seule chaîne. -r (--raw-output) renvoie le contenu des chaînes au lieu des littéraux JSON.

Si l'entrée ne contient pas de sauts de ligne (ou si vous ne voulez pas les échapper en tant que %0A), vous pouvez utiliser uniquement jq -Rr @uri sans l'option -s.

Ou ce pourcentage encode tous les octets:

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

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

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

( source )

29
blueyed

Si vous souhaitez exécuter la requête GET et utiliser le 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
20
Piotr Czapla

C'est peut-être le meilleur:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
15
chenzhiwei

Lien direct vers la version de 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 ([email protected])
# 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 i.e. 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" string (encoded)
    split ("1 2 3 4 5 6 7 8 9 A B C D E F", 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 ""
    }
' "$@"
14
MatthieuP
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 n’ayez pas à le mettre dans un var si vous voulez. BTW n'a pas inclus le sed pour l'onglet pensé qu'il allait le transformer en espaces

10
manoflinux

Voici une solution Bash qui n’appelle 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"
}
8
davidchambers

Pour ceux d'entre vous qui recherchent une solution qui n'a pas besoin de Perl, voici une solution qui nécessite uniquement 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 } # strip newlines
     $2 ~  /^[a-zA-Z0-9.*()\/-]$/  { printf("%s",   $2);  next } # pass through what we can
                                   { printf("%%%s", $1)        } # take hex value of everything else
   '`
}

Cousus ensemble à partir de quelques endroits sur le net et de quelques essais et erreurs locaux. Cela fonctionne très bien!

7
Louis Marascio

ni2ascii est très pratique:

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

Vous pouvez émuler le javascript encodeURIComponent en 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 diriger vers encodeURIComponent:

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

Si vous ne voulez pas dépendre de Perl, vous pouvez également utiliser sed. C'est un peu brouillon, 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 codage, et la partie correcte, codera la partie qui en a besoin, puis recousue.

Vous pouvez le mettre dans un script sh pour plus de commodité, peut-être qu'il prenne un paramètre à encoder, le place sur votre chemin et vous pouvez simplement appeler:

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

source

6
Jay

Utiliser php à partir d'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
6
Darren Weber

La question porte sur le faire dans bash et il n’est pas nécessaire d'utiliser python ou Perl car il existe en fait une seule commande qui fait exactement ce que vous voulez - "urlencode".

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

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

Notez que vous avez besoin de "gridsite-clients" pour fournir cette commande.

5
Dylan

Simple PHP option:

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

Voici la version du noeud:

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

Une autre approche php:

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

Ruby, pour être complet

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

Voici ma version pour busybox ash Shell pour un système embarqué, j'avais initialement 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 <string>
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}
3
nulleight

Voici une conversion d'une ligne utilisant Lua, similaire à réponse de blueyed sauf avec 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 peut-être vous assurer que les nouvelles lignes de votre chaîne sont converties de LF en CRLF, auquel cas vous pouvez insérer un gsub("\r?\n", "\r\n") dans la chaîne avant le codage en pourcentage.

Voici une variante qui, dans le style non standard d'application/x-www-form-urlencoded , effectue cette normalisation des nouvelles lignes, ainsi que l'encodage des espaces en tant que '+' au lieu de '% 20' ( qui pourrait probablement être ajouté à l’extrait de code Perl en 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")
2
Stuart P. Bentley

Voici une fonction POSIX pour le faire:

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

Exemple:

value=$(encodeURIComponent "$2")

Source

2
Steven Penny

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

#!/bin/ksh93

function rawurlencode
{
    typeset string="${1}"
    typeset strlen=${#string}
    typeset encoded=""

    for (( pos=0 ; pos<strlen ; pos++ )); do
        c=${string:$pos:1}
        case "$c" in
            [-_.~a-zA-Z0-9] ) o="${c}" ;;
            * )               o=$(printf '%%%02x' "'$c")
        esac
        encoded+="${o}"
    done
    print "${encoded}"
}

function rawurldecode
{
    printf $(printf '%b' "${1//%/\\x}")
}

print $(rawurlencode "C++")     # --> C%2b%2b
print $(rawurldecode "C%2b%2b") # --> C++
1
Ray Burgemeestre

Ayant php installé j'utilise cette façon:

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

Qu'est-ce qui analyserait les URL mieux que javascript?

node -p "encodeURIComponent('$url')"
0
Nestor Urquiza

Ce qui suit est basé sur la réponse d'Orwellophile, mais résout le bogue multi-octets mentionné dans les commentaires en définissant LC_ALL = C (une astuce de vte.sh). Je l'ai écrit sous forme de fonction convenable Prompt_COMMAND, car c'est ainsi que je l'utilise.

print_path_url() {
  local LC_ALL=C
  local string="$PWD"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9/] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  printf "\033]7;file://%s%s\007" "${HOSTNAME:-}" "${encoded}"
}
0
Per Bothner