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 $@

Voir aussi: Comment décoder une chaîne encodée URL dans le shell? pour les solutions sans boucle.
kenorb

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

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

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!


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'

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'

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/'

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

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 )


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

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 (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 "$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 ""
    }
' "$@"

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")

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"
}

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


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!


7

uni2ascii est très pratique:

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

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


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!

6

Voici la version du nœud:

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

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.


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);'

4

Ruby, pour l'exhaustivité

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

4

Une autre approche php:

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

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}"
}

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


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")

1

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

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

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++

1

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

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

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}"
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.