Comment urlencode les données pour la commande curl?

319

J'essaie d'écrire un script bash pour les tests qui prend un paramètre et l'envoie via curl au site Web. J'ai besoin d'url encoder la valeur 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 $@
Aaron
la source

Réponses:

396

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

Cela publie des données, similaires aux autres --dataoptions, à l'exception du fait qu'il effectue le codage d'URL. Pour être conforme à CGI, la <data>pièce 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

Consultez 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 -Vpour vérifier quelle version vous avez.

Vous pouvez également encoder la chaîne de requête :

curl -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202
Jacob Rask
la source
5
Semble fonctionner uniquement pour http POST. Documentation ici: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James
82
@StanJames Si vous l'utilisez comme ça, curl peut également faire l'encodage pour une requête GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
kberg
13
@kberg en fait, cela ne fonctionnera que pour les données de requête. curl ajoutera un '?' suivi par les paramétrés urlencoded. Si vous voulez encoder en url un suffixe d'URL (tel qu'un CouchDB GET pour un identifiant de document), alors '--data-urlencode' ne fonctionnera pas.
Bokeh
1
Ne fonctionne pas pour curl --data-urlencode "description=![image]($url)" www.example.com. Une idée pourquoi? `
Khurshid Alam
1
@NadavB Escaping "the‽
BlackJack
179

Voici la pure réponse 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 correspondante rawurldecode (), 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 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 (eh bien, il ira beaucoup plus vite, et pourrait faire des fichiers binaires et autres ...) J'ai trouvé cela 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
Orwellophile
la source
4
Malheureusement, ce script échoue sur certains caractères, tels que 'é' et '½', produisant respectivement 'e% FFFFFFFFFFFFFFCC' et '% FFFFFFFFFFFFFFC2' (b / c de la boucle par caractère, je crois).
Matthemattics
1
Cela ne fonctionne pas pour moi dans Bash 4.3.11 (1). La chaîne Jogging «à l'Hèze»génère Jogging%20%abà%20l%27Hèze%bbqui ne peut pas être decodeURIComponent
envoyée
2
Dans ce premier bloc de code, que signifie le dernier paramètre à imprimer? Autrement dit, pourquoi est-il guillemet double, guillemet simple, signe dollar, lettre c, guillemet double? Est-ce que le guillemet simple fait?
Colin Fraizer
1
@dmcontador - ce n'est qu'un humble script bash, il n'a aucune conception de caractères multi-octets ou unicode. Lorsqu'il voit un personnage comme ń ( \u0144), il affiche naïvement% 144, ╡ ( \u2561) est généré en tant que% 2561. Les bonnes réponses codées en brut pour celles-ci seraient respectivement% C5% 84% 0A et% E2% 95% A1.
Orwellophile
1
@ColinFraizer le guillemet simple sert à convertir le caractère suivant en sa valeur numérique. réf. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Sam
94

Utilisez le URI::Escapemodule et la uri_escapefonction de Perl 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!

dubek
la source
2
URI :: Escape n'est peut-être pas installé, vérifiez ma réponse dans ce cas.
blueyed
J'ai corrigé cela (utilisation echo, pipe et <>), et maintenant cela fonctionne même lorsque $ 2 contient une apostrophe ou des guillemets doubles. Merci!
dubek
9
Vous supprimez echoaussi:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen
1
La version de Chris Johnsen est meilleure. J'avais $ {True} dans mon expression de test et l'utiliser via echo a déclenché l'expansion de la variable uri_escape / Perl.
mm2001
1
@ jrw32982 oui, en y repensant, avoir une autre langue avec laquelle accomplir cette tâche est une bonne chose. Si je le pouvais, je reprendrais mon downvote, mais hélas il est actuellement bloqué.
thecoshman
69

Une autre option consiste à utiliser jq(comme filtre):

jq -sRr @uri

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

Si l'entrée n'est pas la sortie d'une autre commande, vous pouvez la stocker dans une jqvariable de chaîne:

jq -nr --arg v "my shell string" '$v|@uri'

-n( --null-input) ne lit pas l'entrée et la --arg name valuestocke valuedans une variable namesous forme de chaîne. Dans le filtre, $name(entre guillemets simples, pour éviter l'expansion par le shell), référence la variable name.

Enveloppé comme une fonction Bash, cela devient:

function uriencode { jq -nr --arg v "$1" '$v|@uri'; }

Ou ce pourcentage encode tous les octets:

xxd -p|tr -d \\n|sed 's/../%&/g'
nisetama
la source
3
<3 il ... devrait être le meilleur et accepté IMO (ouais si vous pouvez dire curlpour coder cela fonctionne et si bash a un intégré qui aurait été acceptable - mais jqsemble être un bon ajustement alors que je suis loin d'atteindre le niveau de confort avec cet outil)
nhed
5
pour ceux qui se demandent la même chose que moi: @urin'est pas une variable, mais un filtre jq littéral utilisé pour formater les chaînes et s'échapper; voir le manuel jq pour plus de détails (désolé, pas de lien direct, besoin de chercher @urisur la page ...)
ssc
la version xxd est exactement le genre de chose que je cherchais. Même s'il est un peu sale, il est court et n'a aucune dépendance
Rian Sanderson
1
Un exemple d'utilisation de jq pour encoder en url:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal
67

dans un souci d'exhaustivité, de nombreuses solutions utilisant sedou awkne traduisent qu'un ensemble spécial de caractères et sont donc assez grandes par la taille du code et ne traduisent pas non plus d'autres caractères spéciaux qui devraient être encodés.

un moyen sûr de coder en url serait de simplement 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 veille ici à ce que l'entrée soit traitée comme des octets et non comme des caractères.

Éditer:

xxd est livré 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 à hexdumppartir 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 à la hexdumpplace de xxdet permet d'éviter l' trappel:

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
josch
la source
1
xxd -plaindevrait arriver APRÈS tr -d '\n'!
qdii
3
@qdii pourquoi? cela rendrait non seulement impossible l'urlencode des sauts de ligne, mais cela insèrerait également à tort des sauts de ligne créés par xxd dans la sortie.
josch
1
@josch. Ceci est tout simplement faux. Tout d'abord, tous les \ncaractères seront traduits par xxd -plainen 0a. Ne me croyez pas sur parole, essayez-le vous-même: echo -n -e '\n' | xxd -plaincela prouve que votre tr -d '\n'est inutile ici car il ne peut pas y en avoir \naprès xxd -plain Second, echo foobarajoute son propre \ncaractère à la fin de la chaîne de caractères, donc xxd -plainne se nourrit pas foobarcomme prévu mais avec foobar\n. puis le xxd -plain traduit en une chaîne de caractères qui se termine par 0a, ce qui le rend inapproprié pour l'utilisateur. Vous pourriez ajouter -nà echole résoudre.
qdii
6
@qdii en effet -n manquait pour l'écho mais l' xxdappel appartient en face de l' tr -dappel. Il y appartient pour que toute nouvelle ligne en foobarsoit traduite par xxd. L' tr -daprès l' xxdappel consiste à supprimer les retours à la ligne produits par xxd. Il semble que vous n'ayez jamais de foobar assez longtemps pour que cela xxdproduise des retours à la ligne, mais pour de longues entrées, cela le sera. Donc, tr -dc'est nécessaire. Contrairement à votre hypothèse, tr -dil ne s'agissait PAS de supprimer les retours à la ligne de l'entrée mais de la xxdsortie. Je souhaite conserver les sauts de ligne en entrée. Votre seul point valable est que l'écho ajoute une nouvelle ligne inutile.
josch
1
@qdii et aucune infraction prise - je pense juste que vous vous trompez, sauf pour ce echo -nqui me manquait en effet
josch
62

L'une des variantes, peut être moche, 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 one-liner 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/'
Sergey
la source
1
Je pense que c'est une façon très intelligente de réutiliser le codage URL de cURL.
solidsnack
13
C'est absolument génial! Je souhaite vraiment que vous ayez laissé une ligne pour que les gens puissent voir à quel point c'est vraiment simple. Pour URL encoder le résultat de la datecommande… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Vous devez cutdésactiver les 2 premiers caractères, car la sortie de curl est techniquement une URL relative avec une chaîne de requête.)
Bruno Bronosky
2
@BrunoBronosky Votre variante à une ligne est bonne mais ajoute apparemment un "% 0A" à la fin de l'encodage. Attention aux utilisateurs. La version de la fonction ne semble pas avoir ce problème.
levigroker
7
Pour éviter %0Aà la fin, utilisez printfau lieu de echo.
kenorb
2
le paquebot est fantastique
Stephen Blum
49

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 nuiront pas. urllib est dans la bibliothèque standard. Cela fonctionne pour exampple pour cette url folle (du monde réel):

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
sandro
la source
2
J'ai eu quelques problèmes avec les guillemets et les caractères spéciaux avec le triplequoting, cela semblait fonctionner pour pratiquement tout: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. write (urllib.quote (sys.stdin.read ())) ")";
Arrêtez de calomnier Monica Cellio
La version Python 3 serait encoded_value=$(python3 -c "import urllib.parse; print (urllib.parse.quote('''$value'''))").
Creshal
1
python -c 'import urllib, sys; sys.stdout.writelines(urllib.quote_plus(l, safe="/\n") for l in sys.stdin)'n'a presque aucun problème de citation et devrait être efficace en
termes de
2
Il serait beaucoup plus sûr de s'y référer sys.argvplutôt que de se substituer $valueà une chaîne plus tard analysée en tant que code. Et s'il était valuecontenu ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy
2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite
30

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 )

bleuté
la source
4
travaillé pour moi. Je l'ai changé en perl -lpe ... (la lettre ell). Cela a supprimé la nouvelle ligne de fin, dont j'avais besoin pour mes besoins.
JohnnyLambada
2
Pour info, pour faire l'inverse, utilisez perl -pe 's/\%(\w\w)/chr hex $1/ge'(source: unix.stackexchange.com/questions/159253/… )
Sridhar Sarnobat
2
En fonction des caractères spécifiques à coder, vous pouvez simplifier ce perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'qui autorise les lettres, les chiffres et les traits de soulignement, mais encode tout le reste.
robru
23

Si vous souhaitez exécuter la GETdemande et utiliser la boucle pure, 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
Piotr Czapla
la source
15

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 ([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 ""
    }
' "$@"
MatthieuP
la source
Existe-t-il une variante simple pour obtenir le codage UTF-8 au lieu de l'ASCII?
avgvstvs
15

Cela peut être le meilleur:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
chenzhiwei
la source
Cela fonctionne pour moi avec deux ajouts: 1. remplacez le -e par -n pour éviter d'ajouter une nouvelle ligne à la fin de l'argument et 2. ajoutez '%%' à la chaîne printf pour mettre un% devant chaque paire de chiffres hexadécimaux.
Rob Fagen
fonctionne après avoir ajouté la tranche $ ahead after=$(echo -e ...
Roman Rhrn Nesterov
1
Veuillez expliquer comment cela fonctionne. La odcommande n'est pas courante.
Mark Stosberg
Cela ne fonctionne pas avec OS X odcar il utilise un format de sortie différent de GNU od. Par exemple, printf aa|od -An -tx1 -v|tr \ -imprime -----------61--61--------------------------------------------------------avec OS X odet -61-61avec GNU od. Vous pouvez utiliser od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\navec OS X odou GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nfait la même chose, même s'il xxdn'est pas dans POSIX mais l' odest.
nisetama
2
Bien que cela puisse fonctionner, cela échappe à tous les personnages
Charlie
11

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"
}
davidchambers
la source
4
Cela se comporte différemment entre les versions bash. Sur RHEL 6.9, le bash est 4.1.2 et il inclut les guillemets simples. Alors que Debian 9 et bash 4.4.12 conviennent parfaitement aux guillemets simples. Pour moi, la suppression des guillemets simples l'a fait fonctionner sur les deux. s = "$ {s // ',' /% 2C}"
muni764
1
J'ai mis à jour la réponse pour refléter votre conclusion, @ muni764.
davidchambers
Juste un avertissement ... cela n'encodera pas des choses comme le personnageá
diogovk
10
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 l'afficher dans $ url. bien que vous n'ayez pas à le mettre dans un var si vous voulez. BTW n'a pas inclus le sed pour tab pensait que cela le transformerait en espaces

manoflinux
la source
5
J'ai l'impression que ce n'est pas la façon recommandée de le faire.
Cody Gray
2
expliquez votre sentiment s'il vous plaît .... parce que ce que j'ai déclaré fonctionne et je l'ai utilisé dans plusieurs scripts, donc je sais que cela fonctionne pour tous les caractères que j'ai énumérés. veuillez expliquer pourquoi quelqu'un n'utiliserait pas mon code et n'utiliserait pas perl puisque le titre de ceci est "URLEncode à partir d'un script bash" et non un script perl.
manoflinux
Parfois, aucune solution de perles n'est nécessaire, donc cela peut être utile
Yuval Rimar
3
Ce n'est pas la manière recommandée de le faire car la liste noire est une mauvaise pratique, et c'est de toute façon inamical unicode.
Ekevoo
C'était la solution la plus conviviale compatible avec cat
file.txt
7

Pour ceux d'entre vous qui recherchent 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="$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
   '`
}

Assemblé à partir de quelques endroits à travers le net et des essais et erreurs locaux. Ça marche super!

Louis Marascio
la source
7

uni2ascii est très pratique:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
kev
la source
2
Cela ne fonctionne pas pour les caractères à l' intérieur de la plage ASCII, qui nécessitent des guillemets, comme %et de l'espace (ce dernier peut être corrigé avec le -sdrapeau)
Boldewyn
7

Si vous ne voulez pas dépendre de Perl, vous pouvez également utiliser sed. C'est un peu compliqué, car chaque personnage doit être échappé individuellement. Créez un fichier avec le contenu suivant et appelez-leurlencode.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 qui a besoin d'encodage, et la partie qui va bien, codera la partie qui en a besoin, puis recoudra ensemble.

Vous pouvez mettre cela dans un script sh pour plus de commodité, peut-être lui faire prendre un paramètre pour encoder, le mettre sur votre chemin et ensuite vous pouvez simplement appeler:

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

la source

Geai
la source
7

Vous pouvez émuler du javascript encodeURIComponenten perl. Voici la commande:

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

Vous pouvez maintenant accéder à encodeURIComponent:

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

Voici la version du nœud:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\\'}')"
}
davidchambers
la source
1
Cette rupture ne se produira-t-elle pas s'il y a d'autres caractères dans la chaîne qui ne sont pas valides entre guillemets simples, comme une barre oblique inverse unique ou des retours à la ligne?
Stuart P. Bentley
Bon point. Si nous devons nous efforcer d'échapper à tous les personnages problématiques dans Bash, nous pourrions aussi bien effectuer les remplacements directement et éviter nodecomplètement. J'ai posté une solution Bash uniquement. :)
davidchambers
1
Cette variante trouvée ailleurs sur la page évite le problème de citation en lisant la valeur de STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg
6

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 beaucoup mieux, car la réponse Perl ci-dessus, par exemple, n'encode pas tous les caractères correctement. Essayez-le avec le long tiret que vous obtenez de Word et vous obtenez le mauvais encodage.

Remarque, vous devez installer "gridsite-clients" pour fournir cette commande.

Dylan
la source
1
Ma version de bash (GNU 3.2) n'en a pas urlencode. Quelle version utilisez-vous?
Sridhar Sarnobat
1
J'ai 4.3.42, mais la commande urlencode est fournie par "gridsite-clients". Essayez d'installer cela et ça devrait aller.
Dylan
5
Votre réponse n'est donc pas meilleure que celles qui nécessitent l'installation d'autres choses (python, perl, lua,…)
Cyrille Pontvieux
Sauf qu'il ne nécessite que l'installation d'un seul utilitaire au lieu d'une langue entière (et de bibliothèques), plus est super simple et clair pour voir ce qu'il fait.
Dylan
Un lien vers la page package / projet fournissant cette commande aurait été utile.
Doron Behar
6

Option PHP simple:

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

Ruby, pour l'exhaustivité

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

Une autre approche php:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
jan halfar
la source
2
echoajoutera un caractère de nouvelle ligne (hex 0xa). Pour l'empêcher de faire cela, utilisez echo -n.
Mathew Hall
3

Voici ma version pour shell ashbox occupé 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 <string>
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}
nulleight
la source
2

Voici une fonction POSIX pour ce 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")

La source

Steven Penny
la source
2

Voici une conversion d'une ligne utilisant Lua, similaire à la réponse de Blueyed, sauf que tous les caractères RFC 3986 non réservés ne sont pas codés (comme cette réponse ):

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

En outre, vous devrez peut-être vous assurer que les sauts de ligne dans votre chaîne sont convertis de LF en CRLF, auquel cas vous pouvez insérer un gsub("\r?\n", "\r\n") dans la chaîne avant le pourcentage de codage.

Voici une variante qui, dans le style d'application non standard / x-www-form-urlencoded , effectue cette normalisation de nouvelle ligne, ainsi que le codage des espaces en tant que '+' au lieu de '% 20' (qui pourrait probablement être ajouté à la Extrait de 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")
Stuart P. Bentley
la source
1

Après avoir installé php, j'utilise de cette façon:

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

Il s'agit de la version ksh de la réponse d'orwellophile contenant les fonctions rawurlencode et rawurldecode (lien: Comment urlencoder des données pour la commande curl? ). Je n'ai pas assez de représentant pour poster un commentaire, d'où le nouveau message ..

#!/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++
Ray Burgemeestre
la source
1

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

node -p "encodeURIComponent('$url')"
Nestor Urquiza
la source
Portée hors de question. Pas bash, pas curl. Même si je suis sûr que cela fonctionne très bien si le nœud est disponible.
Cyrille Pontvieux
Pourquoi voter contre cela et pas les réponses python / perl? En outre, comment cela ne répond pas à la question d'origine "Comment urlencode les données pour la commande curl?". Cela peut être utilisé à partir d'un script bash et le résultat peut être donné à une commande curl.
Nestor Urquiza
J'ai également rejeté les autres. La question était de savoir comment faire cela dans un script bash. Si un autre langage est utilisé comme node / js, python ou perl, il n'est alors pas nécessaire d'utiliser curl directement.
Cyrille Pontvieux
2
Bien que je n'aie pas pris la peine de downvote, le problème avec cette commande est qu'elle nécessite que les données soient correctement échappées pour être utilisées en javascript. Comme essayez-le avec des guillemets simples et de la folie anti-slash. Si vous voulez utiliser le nœud, vous feriez mieux de lire des trucs de stdin commenode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - pirate
1
Soyez prudent avec la solution de @ MichaelKrelin-hacker si vous transférez des données depuis STDIN, assurez-vous de ne pas inclure de nouvelle ligne de fin. Par exemple, echo | ...est incorrect, tout en echo -n | ...supprimant la nouvelle ligne.
Mark Stosberg
0

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 adaptée 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}"
}
Per Bothner
la source