Comment faire pour les données urlencode pour la commande curl?

j'essaie d'écrire un script bash pour tester qui prend un paramètre et l'envoie par curl vers le site web. J'ai besoin d'encoder la valeur pour s'assurer que les caractères spéciaux sont correctement traitées. Quelle est la meilleure façon de le faire?

Voici mon script de base:

#!/bin/bash
host=${1:?'bad host'}
value=
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@
246
demandé sur Mateusz Piotrowski 2008-11-17 22:09:59

30 réponses

utiliser curl --data-urlencode ; à partir de man curl :

cette option affiche des données, similaires aux autres options --data à l'exception que cela effectue le codage D'URL. Pour être conforme à la norme CGI, la partie <data> doit commencer par un nom suivi d'un séparateur et d'une spécification de contenu.

exemple d'usage:

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

Voir à la page man pour plus d'info.

il faut curl 7.18.0 ou plus récent (janvier 2008) . Utilisez curl -V pour vérifier quelle version vous avez.

303
répondu Jacob R 2017-08-14 18:41:56

Voici la réponse pure BASH.

rawurlencode() {
  local string=""
  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 façons:

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

[édité]

Voici la fonction rawurldecode() correspondant, qui - avec toute modestie - est impressionnant.

# 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 l'ensemble correspondant, nous pouvons maintenant effectuer quelques tests simples:

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

Output: Matched

Et si vraiment vous sentez vraiment que vous avez besoin d'un outil externe (eh bien, aller beaucoup plus vite, et susceptible de faire des fichiers binaires et... J'ai trouvé ça sur mon routeur OpenWRT...

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

où url_escape.sed était un fichier qui contenait 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
139
répondu Orwellophile 2016-03-05 05:58:14

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

...

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

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

86
répondu dubek 2010-01-12 09:39:32

pour des raisons d'exhaustivité, de nombreuses solutions utilisant sed ou awk ne traduisent qu'un ensemble spécial de caractères et sont donc assez grandes par taille de code et ne traduisent pas non plus d'autres caractères spéciaux qui devraient être encodés.

une façon sûre d'encoder urlencode 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/\(..\)/%/g'

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

edit:

xxd est fourni avec le paquet vim-common dans Debian et j'étais 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 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 ici 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/\(..\)/%/g'
52
répondu josch 2014-02-12 20:33:12

je le trouve plus lisible en python:

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

le triple ' garantit que les devis simples en valeur ne fera pas de mal. urllib est dans la bibliothèque standard. Cela fonctionne pour exampple pour ce fou (monde réel) url:

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
41
répondu sandro 2010-02-10 10:26:10

une des variantes, peut être laid, mais simple:

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

Voici la version à une doublure par exemple (comme suggéré par Bruno ):

date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-
38
répondu Sergey 2018-05-01 23:56:54

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

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

( source )

27
répondu blueyed 2013-05-31 16:27:49

si vous souhaitez lancer la requête GET et utiliser du curl pur, il suffit d'ajouter --get à la solution de @Jacob's.

voici un exemple:

curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
17
répondu Piotr Czapla 2011-02-25 12:37:16

Une autre option est d'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 seule chaîne. -r ( --raw-output ) affiche le contenu des chaînes au lieu des chaînes JSON.

ou ce pourcentage-code tous les octets:

xxd -p|tr -d \n|sed 's/../%&/g'
15
répondu user4669748 2015-12-22 02:38:16

lien Direct vers la version awk: http://www.shelldorado.com/scripts/cmds/urlencode

Je l'ai utilisé pendant des années et il 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 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 ""151900920""`          # 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 "" 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 ("151900920"); ++i ) {
        c = substr ("151900920", 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
répondu MatthieuP 2014-11-09 00:17:39

C'est peut-être le meilleur:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
12
répondu chenzhiwei 2017-07-12 03:08:17
url=$(echo "" | 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 encodera la chaîne à l'intérieur de $1 et la produira 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

9
répondu manoflinux 2011-01-11 13:27:13

pour ceux d'entre vous qui cherchent une solution qui n'a pas besoin de perl, en voici une qui n'a besoin que de hexdump et awk:

url_encode() {
 [ $# -lt 1 ] && { return; }

 encodedurl="";

 # 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 '
      == "20"                    { printf("%s",   "+"); next } # space becomes plus
      ~  /0[adAD]/               {                      next } # strip newlines
      ~  /^[a-zA-Z0-9.*()\/-]$/  { printf("%s",   );  next } # pass through what we can
                                   { printf("%%%s", )        } # take hex value of everything else
   '`
}

cousu ensemble à partir de quelques endroits à travers le net et certains essais et erreurs locales. Il fonctionne très bien!

7
répondu Louis Marascio 2012-03-27 19:28:54

utilisant 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
répondu Darren Weber 2012-01-31 23:10:59

uni2ascii est très pratique:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
6
répondu kev 2012-11-26 08:48:57

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éer un fichier avec le contenu suivant et l'appeler 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, faites ce qui 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 qui a besoin d'encodage, et la partie qui est fine, encodera la partie qui en a besoin, puis recousera ensemble.

You peut mettre cela dans un script sh pour la commodité, peut-être le faire prendre un paramètre à encoder, le mettre sur votre chemin et puis vous pouvez juste appeler:

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

source

6
répondu Jay 2014-08-26 08:07:15

la question Est de faire cela en bash et il n'y a pas besoin de python ou perl car il y a en fait une seule commande qui fait exactement ce que vous voulez - "urlencode".

value=$(urlencode "")

c'est aussi beaucoup mieux, car la réponse perl ci-dessus, par exemple, ne Code pas tous les caractères correctement. Essayez avec le long dash que vous obtenez de Word et vous obtenez le mauvais encodage.

Note, Vous avez besoin de "gridsite-clients" installé pour fournir cette commande.

5
répondu Dylan 2014-11-14 11:55:29

vous pouvez imiter javascript encodeURIComponent en perl. Voici la commande:

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

vous pouvez définir ceci comme un alias bash dans .bash_profile :

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

Maintenant vous pouvez pipe dans encodeURIComponent :

$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!
5
répondu Klaus 2015-01-20 21:08:51

Simple option PHP:

echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'
5
répondu Ryan 2015-02-26 09:07:20

un Autre php approche:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
4
répondu jan halfar 2013-12-18 09:13:49

Voici la version du noeud:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\'}')"
}
4
répondu davidchambers 2014-11-06 07:49:18

Ruby, par souci d'exhaustivité

value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "")"
3
répondu k107 2012-06-19 23:45:26

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"
}
3
répondu davidchambers 2018-05-23 21:01:03

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:]_.!~*()-]/ ? q y : q sprintf("%%%02X", z[y])
  print q}' ""
}

exemple:

value=$(encodeURIComponent "")

Source

2
répondu Steven Penny 2016-12-31 05:14:25

voici une conversion d'une ligne en utilisant Lua, similaire à réponse de blueyed sauf avec tous les RFC 3986 caractères non réservés gauche non codé (comme cette réponse ):

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

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 pourcentage d'encodage.

Voici une variante qui , dans le style non standard de l'application/x-www-form-urlencoded , fait que la normalisation newline, ainsi que l'encodage des espaces comme '+' au lieu de '%20' (qui pourrait probablement être ajouté à L'extrait 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 - "")
2
répondu Stuart P. Bentley 2017-05-23 11:47:26

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

URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"`
1
répondu ajaest 2013-08-15 12:04:33

C'est le ksh version de orwellophile de la réponse contenant la rawurlencode et rawurldecode fonctions (lien: Comment urlencode de 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 string=""
    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
répondu Ray Burgemeestre 2017-05-23 12:26:20

voici ma version pour BusyBox Ash shell pour un système embarqué, j'ai à l'origine adopté la variante D'Orwellophile:

urlencode()
{
    local S=""
    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}"
}
1
répondu nulleight 2017-01-31 10:45:24

ce qui suit est basé sur la réponse D'Orwellophile, mais résout le multibyte bug mentionné dans les commentaires en définissant LC_ALL=C (une astuce de vte.sh). Je l'ai écrit sous la forme de la fonction INVIT_COMMAND convenable, parce que c'est la façon dont 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 "3]7;file://%s%s"151900920"7" "${HOSTNAME:-}" "${encoded}"
}
0
répondu Per Bothner 2018-01-03 04:56:48

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

node -p "encodeURIComponent('$url')"
-1
répondu Nestor Urquiza 2017-04-12 17:42:15