¿Cómo urlencode datos para el comando curl?


319

Estoy tratando de escribir un script bash para la prueba que toma un parámetro y lo envía a través de curl al sitio web. Necesito url codificar el valor para asegurarme de que los caracteres especiales se procesen correctamente. ¿Cuál es la mejor manera de hacer esto?

Aquí está mi script básico hasta ahora:

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

Ver también: ¿Cómo decodificar cadenas codificadas por URL en shell? para soluciones sin rizos.
kenorb

Respuestas:


396

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

Esto publica datos, similares a las otras --dataopciones con la excepción de que esto realiza la codificación de URL. Para cumplir con CGI, la <data>parte debe comenzar con un nombre seguido de un separador y una especificación de contenido.

Ejemplo de uso:

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

Vea la página del manual para más información.

Esto requiere curl 7.18.0 o posterior (lanzado en enero de 2008) . Use curl -Vpara verificar qué versión tiene.

También puede codificar la cadena de consulta :

curl -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202

55
Parece que solo funciona para http POST. Documentación aquí: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James

82
@StanJames Si lo usa así, curl también puede codificar una solicitud GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
kberg

13
@kberg en realidad, esto solo funcionará para los datos de consulta. curl agregará un '?' seguido por los parámetros urlencoded. Si desea codificar urlen algún postfix de url (como CouchDB GET para alguna identificación de documento), entonces '--data-urlencode' no funcionará.
Bokeh

1
No funciona para curl --data-urlencode "description=![image]($url)" www.example.com. ¿Alguna idea de por qué? `
Khurshid Alam

1
@NadavB Escaping "the‽
BlackJack

179

Aquí está la respuesta BASH pura.

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
}

Puedes usarlo de dos maneras:

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

[editado]

Aquí está la función rawurldecode (), que, con toda modestia, es increíble.

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

Con el conjunto correspondiente, ahora podemos realizar algunas pruebas simples:

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

Output: Matched

Y si realmente siente que necesita una herramienta externa (bueno, irá mucho más rápido y podría hacer archivos binarios y tal ...) Encontré esto en mi enrutador OpenWRT ...

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

Donde url_escape.sed era un archivo que contenía estas reglas:

# 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

44
Desafortunadamente, este script falla en algunos caracteres, como 'é' y '½', generando 'e% FFFFFFFFFFFFFFCC' y '% FFFFFFFFFFFFFFC2', respectivamente (b / c del ciclo por carácter, creo).
Matthemattics

1
No funciona para mí en Bash 4.3.11 (1). La cadena Jogging «à l'Hèze»genera Jogging%20%abà%20l%27Hèze%bbque no se puede alimentar a JS decodeURIComponent:(
dmcontador

2
En ese primer bloque de código, ¿qué significa el último parámetro para imprimir? Es decir, ¿por qué es comillas dobles, comillas simples, signo de dólar, letra c, comillas dobles? ¿La comilla simple hace?
Colin Fraizer

1
@dmcontador: es solo un humilde script de bash, no tiene una concepción de caracteres de varios bytes o unicode. Cuando vea un carácter como ń ( \u0144), generará ingenuamente% 144, ╡ ( \u2561) se generará como% 2561. Las respuestas correctas sin codificar para estas serían% C5% 84% 0A y% E2% 95% A1 respectivamente.
Orwellophile

1
@ColinFraizer, la comilla simple sirve para convertir el siguiente carácter en su valor numérico. árbitro. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Sam

94

Use el URI::Escapemódulo y la uri_escapefunción de Perl en la segunda línea de su script bash:

...

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

Editar: Solucione problemas de citas, como lo sugiere Chris Johnsen en los comentarios. ¡Gracias!


2
Es posible que URI :: Escape no esté instalado, verifique mi respuesta en ese caso.
azulado

Lo arreglé (use echo, pipe y <>), y ahora funciona incluso cuando $ 2 contiene un apóstrofe o comillas dobles. ¡Gracias!
dubek

99
Usted también elimina echo:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen

1
La versión de Chris Johnsen es mejor. Tenía $ {True} en mi expresión de prueba y el uso de esto a través de eco disparó la expansión de variable uri_escape / Perl.
mm2001

1
@ jrw32982 sí, volviendo la vista atrás, tener otro idioma con el que realizar esta tarea es bueno. Si pudiera, me llevaría de vuelta a mi downvote, pero por desgracia está bloqueado actualmente.
thecoshman

69

Otra opción es usar jq(como filtro):

jq -sRr @uri

-R( --raw-input) trata las líneas de entrada como cadenas en lugar de analizarlas como JSON y -sR( --slurp --raw-input) lee la entrada en una sola cadena.-r( --raw-output) genera el contenido de cadenas en lugar de literales de cadena JSON.

Si la entrada no es la salida de otro comando, puede almacenarlo en una jqvariable de cadena:

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

-n( --null-input) no lee la entrada, y --arg name valuealmacena valueen variable namecomo una cadena. En el filtro, $name(entre comillas simples, para evitar la expansión por el shell), hace referencia a la variablename .

Envuelto como una función Bash, esto se convierte en:

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

O este porcentaje codifica todos los bytes:

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

3
<3 ... debería ser IMO superior y aceptado (sí, si puede decir curlque codifique eso funciona y si bash tiene un valor incorporado que hubiera sido aceptable, pero jqparece un ajuste adecuado, aunque estoy lejos de alcanzar el nivel de comodidad con esta herramienta)
nhed

55
para cualquiera que se pregunte lo mismo que yo: @urino es una variable, sino un filtro jq literal usado para formatear cadenas y escapar; consulte el manual de jq para obtener más detalles (lo siento, no hay un enlace directo, es necesario buscar @urien la página ...)
ssc

la versión xxd es justo lo que estaba buscando. Incluso si está un poco sucio, es corto y no tiene dependencias
Rian Sanderson

1
Una muestra de uso de jq para codificar url:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal

67

en aras de la integridad, muchas soluciones que utilizan sedoawk solo traducen un conjunto especial de caracteres y, por lo tanto, son bastante grandes por tamaño de código y tampoco traducen otros caracteres especiales que deben codificarse.

Una forma segura de urlencode sería simplemente codificar cada byte, incluso aquellos que se habrían permitido.

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

xxd se encarga aquí de que la entrada se maneje como bytes y no como caracteres.

editar:

xxd viene con el paquete vim-common en Debian y solo estaba en un sistema donde no estaba instalado y no quería instalarlo. La alternativa es usarhexdump desde el paquete bsdmainutils en Debian. De acuerdo con el siguiente gráfico, bsdmainutils y vim-common deberían tener la misma probabilidad de ser instalados:

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

pero, sin embargo, aquí hay una versión que usa en hexdumplugar de xxdy permite evitar la trllamada:

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

1
xxd -plaindebe suceder DESPUÉS tr -d '\n'!
qdii

3
@qdii ¿por qué? eso no solo haría imposible urlencodear nuevas líneas, sino que también insertaría erróneamente nuevas líneas creadas por xxd en la salida.
josch

1
@josch. Esto esta simplemente mal. Primero, cualquier \ncarácter será traducido por xxd -plaina 0a. No confíes en mi palabra, pruébalo tú mismo: echo -n -e '\n' | xxd -plainesto demuestra que tu tr -d '\n'es inútil aquí, ya que no puede haber \ndespués de xxd -plain Second, echo foobaragrega su propio \ncarácter al final de la cadena de caracteres, por xxd -plainlo que no se alimenta con foobarlo esperado sino con foobar\n. luego lo xxd -plain traduce en una cadena de caracteres que termina en 0a, haciéndolo inadecuado para el usuario. Podrías agregar -na echopara resolverlo.
qdii

66
@qdii de hecho -n faltaba para echo pero la xxdllamada pertenece al frente de la tr -dllamada. Pertenece allí para que cualquier nueva línea en foobarsea ​​traducida por xxd. El tr -ddespués de la xxdllamada es eliminar las nuevas líneas que xxd produce. Parece que nunca tiene foobar el tiempo suficiente para que xxdproduzca nuevas líneas, pero para entradas largas lo hará. Entonces el tr -des necesario. En contraste con su suposición, tr -dNO era eliminar las nuevas líneas de la entrada, sino de la xxdsalida. Quiero mantener las nuevas líneas en la entrada. Su único punto válido es que ese eco agrega una nueva línea innecesaria.
josch

1
@qdii y no se ofende, solo creo que estás equivocado, excepto por lo echo -nque realmente me faltaba
josch

62

Una de las variantes, puede ser fea, pero 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
}

Aquí está la versión de una línea, por ejemplo (como lo sugirió 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
Creo que esta es una forma muy inteligente de reutilizar la codificación de URL de cURL.
solidsnack

13
¡Esto es absolutamente brillante! Realmente desearía que hubieras dejado una línea para que la gente pueda ver lo simple que es en realidad. Para codificar en URL el resultado del datecomando ... date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-( cutDebe quitar los primeros 2 caracteres, porque la salida de curl es técnicamente una URL relativa con una cadena de consulta).
Bruno Bronosky

2
@BrunoBronosky Su variante de una línea es buena pero aparentemente agrega un "% 0A" al final de la codificación. Los usuarios tengan cuidado. La versión de la función no parece tener este problema.
levigroker

77
Para evitar %0Aal final, use en printflugar de echo.
Kenorb


49

Lo encuentro más legible en Python:

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

el triple 'asegura que las comillas simples en valor no duelen. urllib está en la biblioteca estándar. Funciona por ejemplo para esta loca url (mundo real):

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

2
Tuve algunos problemas con las comillas y los caracteres especiales con la triple cita, esto parecía funcionar básicamente para todo: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. escribir (urllib.quote (sys.stdin.read ())) ")";
Deja de calumniar a Monica Cellio el

Sería la versión de Python 3 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)'casi no tiene problemas de cotización, y debe ser eficiente en cuanto a memoria / velocidad (no lo he verificado, salvo por entrecerrar los ojos)
Alois Mahdal

2
Sería mucho más seguro hacer referencia en sys.argvlugar de sustituirlo $valueen una cadena que luego se analizó como código. ¿Qué pasa si está valuecontenido ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy

2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite

30

He encontrado el siguiente fragmento útil para pegarlo en una cadena de llamadas de programa, donde URI :: Escape podría no estar instalado:

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

( fuente )


44
trabajó para mi. Lo cambié a perl -lpe ... (la letra ell). Esto eliminó la nueva línea final, que necesitaba para mis propósitos.
JohnnyLambada

2
Para su información, para hacer lo inverso a esto, use perl -pe 's/\%(\w\w)/chr hex $1/ge'(fuente: unix.stackexchange.com/questions/159253/… )
Sridhar Sarnobat

2
Dependiendo específicamente de qué caracteres necesita codificar, puede simplificar esto para perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'permitir letras, números y guiones bajos, pero codifica todo lo demás.
robru

23

Si desea ejecutar la GETsolicitud y usar curl puro, simplemente agregue--get a la solución de @ Jacob.

Aquí hay un ejemplo:

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

15

Enlace directo a la versión awk: http://www.shelldorado.com/scripts/cmds/urlencode
Lo utilicé durante años y funciona de maravilla

:
##########################################################################
# 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 una variación simple para obtener la codificación UTF-8 en lugar de ASCII?
avgvstvs

15

Este puede ser el mejor:

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

Esto funciona para mí con dos adiciones: 1. reemplace -e con -n para evitar agregar una nueva línea al final del argumento y 2. agregue '%%' a la cadena printf para poner un% delante de cada par de dígitos hexadecimales.
Rob Fagen

funciona después de agregar el soporte $ ahead after=$(echo -e ...
Roman Rhrn Nesterov

1
Por favor explique cómo funciona esto. El odcomando no es común.
Mark Stosberg el

Esto no funciona con OS X odporque usa un formato de salida diferente al de GNU od. Por ejemplo, printf aa|od -An -tx1 -v|tr \ -imprime -----------61--61--------------------------------------------------------con OS X ody -61-61con GNU od. Puede usarlo od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\ncon OS X odo GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nhace lo mismo, aunque xxdno está en POSIX pero sí od.
nisetama

2
Aunque esto podría funcionar, escapa a todos los personajes
Charlie, el

11

Aquí hay una solución Bash que no invoca ningún programa externo:

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

44
Esto se comporta de manera diferente entre las versiones de bash. En RHEL 6.9, el bash es 4.1.2 e incluye las comillas simples. Mientras que Debian 9 y bash 4.4.12 está bien con las comillas simples. Para mí, eliminar las comillas simples lo hizo funcionar en ambos. s = "$ {s // ',' /% 2C}"
muni764

1
Actualicé la respuesta para reflejar su hallazgo, @ muni764.
davidchambers

Solo una advertencia ... esto no codificará cosas como el personajeá
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')

esto codificará la cadena dentro de $ 1 y la generará en $ url. aunque no tienes que ponerlo en una var si quieres. Por cierto, no incluyó el sed para tab pensó que lo convertiría en espacios


55
Tengo la sensación de que esta no es la forma recomendada de hacerlo.
Cody Gray

2
explique su sentimiento, por favor ... porque lo que he dicho funciona y lo he usado en varios scripts, así que sé que funciona para todos los caracteres que enumeré. así que explique por qué alguien no usaría mi código y usaría perl ya que el título de esto es "URLEncode from a bash script" no es un script de perl.
manoflinux

a veces no se necesita una solución de perlas, así que esto puede ser útil
Yuval Rimar

3
Esta no es la forma recomendada de hacer esto porque la lista negra es una mala práctica y, de todos modos, es unicode hostil.
Ekevoo

Esta fue la solución más amigable compatible con cat
file.txt


7

Para aquellos de ustedes que buscan una solución que no necesita perl, aquí hay una que solo necesita hexdump y 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
   '`
}

Unidas desde un par de lugares en la red y algunas pruebas y errores locales. ¡Funciona muy bien!


7

uni2ascii es muy útil:

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

2
Esto no funciona para los personajes dentro del rango ASCII, que necesitan comillas, como %y espacio (lo último puede remediarse con la -sbandera)
Boldewyn

7

Si no desea depender de Perl, también puede usar sed. Es un poco desordenado, ya que cada personaje tiene que escapar individualmente. Haga un archivo con los siguientes contenidos y llámelourlencode.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

Para usarlo haga lo siguiente.

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"

Esto dividirá la cadena en una parte que necesita codificación, y la parte que está bien, codifica la parte que la necesita, luego vuelve a unirlas.

Puede poner eso en un script sh por conveniencia, tal vez hacer que tome un parámetro para codificar, ponerlo en su ruta y luego simplemente puede llamar:

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

fuente


7

Puede emular javascript encodeURIComponenten perl. Aquí está el comando:

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

Puede establecer esto como un alias bash en .bash_profile:

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

Ahora puedes conectarlo a encodeURIComponent:

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

6

Aquí está la versión del nodo:

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

1
¿No se romperá esto si hay otros caracteres en la cadena que no sean válidos entre comillas simples, como una barra diagonal inversa o líneas nuevas?
Stuart P. Bentley

Buen punto. Si nos tomamos la molestia de escapar de todos los personajes problemáticos en Bash, también podríamos realizar los reemplazos directamente y evitarlos por nodecompleto. Publiqué una solución solo para Bash. :)
davidchambers

1
Esta variante que se encuentra en otra parte de la página evita el problema de las node -p 'encodeURIComponent(require("fs").readFileSync(0))'
citas

6

La pregunta es sobre hacer esto en bash y no hay necesidad de python o perl ya que de hecho hay un solo comando que hace exactamente lo que quieres: "urlencode".

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

Esto también es mucho mejor, ya que la respuesta perl anterior, por ejemplo, no codifica todos los caracteres correctamente. Pruébelo con el guión largo que obtiene de Word y obtiene la codificación incorrecta.

Tenga en cuenta que necesita instalar "gridsite-clients" para proporcionar este comando.


1
Mi versión de bash (GNU 3.2) no tiene urlencode. Qué versión estás usando?
Sridhar Sarnobat

1
Tengo 4.3.42, pero el comando urlencode es proporcionado por "gridsite-clients". Intenta instalar eso y deberías estar bien.
Dylan

55
Entonces, su respuesta no es mejor que ninguna que requiera que otras cosas
estén

Excepto que solo requiere instalar una única utilidad en lugar de un idioma completo (y bibliotecas), además es súper simple y claro para ver lo que está haciendo.
Dylan

Un enlace primero para la página del paquete / proyecto que proporciona este comando habría sido útil.
Doron Behar

6

Opción PHP simple:

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

4

Ruby, para completar

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

4

Otro enfoque de php:

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

2
echoagregará un carácter de nueva línea (hexadecimal 0xa). Para evitar que haga eso, use echo -n.
Mathew Hall

3

Aquí está mi versión para busybox ash shell para un sistema embebido, originalmente adopté la variante de 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

Aquí hay una función POSIX para hacer eso:

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

Ejemplo:

value=$(encodeURIComponent "$2")

Fuente


2

Aquí hay una conversión de una línea usando Lua, similar a la respuesta de blueyed, excepto con todos los caracteres no reservados RFC 3986 sin codificar (como esta respuesta ):

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

Además, es posible que deba asegurarse de que las nuevas líneas en su cadena se conviertan de LF a CRLF, en cuyo caso puede insertar un gsub("\r?\n", "\r\n")en la cadena antes de la codificación porcentual.

Aquí hay una variante que, en el estilo no estándar de aplicación / x-www-form-urlencoded , realiza esa normalización de nueva línea, así como codifica espacios como '+' en lugar de '% 20' (que probablemente podría agregarse al Fragmento de Perl usando una técnica similar).

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

1

Habiendo instalado php, lo uso de esta manera:

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

1

Esta es la versión ksh de la respuesta de orwellophile que contiene las funciones rawurlencode y rawurldecode (enlace: ¿Cómo urlencode datos para el comando curl? ). No tengo suficiente representante para publicar un comentario, de ahí la nueva publicación ...

#!/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é analizaría mejor las URL que JavaScript?

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

Fuera del alcance de la pregunta operativa. No golpea, no riza. Incluso si estoy seguro funciona muy bien si el nodo está disponible.
Cyrille Pontvieux

¿Por qué rechazar esto y no las respuestas de python / perl? Además, ¿cómo esto no responde a la pregunta original "Cómo codificar datos para el comando curl?". Esto se puede usar desde un script bash y el resultado se puede dar a un comando curl.
Nestor Urquiza el

Yo también voté por los otros. La pregunta era cómo hacer esto en un script bash. Si se usa otro idioma como node / js, python o perl, entonces no es necesario usar curl directamente.
Cyrille Pontvieux

2
Si bien no me molesté en votar, el problema con este comando es que requiere que los datos se escapen correctamente para su uso en JavaScript. Como probarlo con comillas simples y algo de barra invertida. Si quieres usar el nodo, es mejor que leas cosas de stdin comonode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - hacker

1
Tenga cuidado con la solución de @ MichaelKrelin-hacker si está ingresando datos desde STDIN, asegúrese de no incluir una nueva línea final. Por ejemplo, echo | ...está mal, mientras que echo -n | ...suprime la nueva línea.
Mark Stosberg el

0

Lo siguiente se basa en la respuesta de Orwellophile, pero resuelve el error multibyte mencionado en los comentarios configurando LC_ALL = C (un truco de vte.sh). Lo escribí en forma de función adecuada PROMPT_COMMAND, porque así es como lo uso.

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}"
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.