Come urlencode i dati per il comando curl?


319

Sto cercando di scrivere uno script bash per i test che accetta un parametro e lo invia attraverso il ricciolo al sito web. Devo url codificare il valore per assicurarmi che i caratteri speciali vengano elaborati correttamente. Qual è il modo migliore per farlo?

Ecco il mio script di base finora:

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

Vedi anche: Come decodificare una stringa con codifica URL nella shell? per soluzioni non arricciate.
Kenorb,

Risposte:


396

Utilizzare curl --data-urlencode; da man curl:

Questo pubblica dati, simili alle altre --dataopzioni con l'eccezione che questo esegue la codifica URL. Per essere conforme a CGI, la <data>parte dovrebbe iniziare con un nome seguito da un separatore e da una specifica del contenuto.

Esempio di utilizzo:

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

Vedi la pagina man per maggiori informazioni.

Ciò richiede l' arricciatura 7.18.0 o successiva (rilasciata a gennaio 2008) . Usa curl -Vper verificare quale versione hai.

Puoi anche codificare la stringa di query :

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
Sembra funzionare solo per HTTP POST. Documentazione qui: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James

82
@StanJames Se lo usi in questo modo curl può anche fare la codifica per una richiesta GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
kberg,

13
@kberg in realtà, funzionerà solo per i dati delle query. il ricciolo aggiungerà un '?' seguito dai parametri codificati. Se desideri urlencode alcuni URL postfix (come CouchDB GET per alcuni ID documento), allora '--data-urlencode' non funzionerà.
Bokeh,

1
Non funziona per curl --data-urlencode "description=![image]($url)" www.example.com. Qualche idea sul perché? `
Khurshid Alam,

1
@NadavB Escaping "the‽
BlackJack

179

Ecco la pura risposta 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
}

Puoi usarlo in due modi:

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

[modificato]

Ecco la corrispondente funzione rawurldecode (), che - con tutta la modestia - è fantastica.

# 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 il set di corrispondenza, ora possiamo eseguire alcuni semplici test:

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

Output: Matched

E se davvero senti di aver bisogno di uno strumento esterno (beh, andrà molto più veloce e potrebbe fare file binari e simili ...) L'ho trovato sul mio router OpenWRT ...

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

Dove url_escape.sed era un file che conteneva queste regole:

# 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
Sfortunatamente, questo script fallisce su alcuni personaggi, come 'é' e '½', producendo 'e% FFFFFFFFFFFFFFCC' e '% FFFFFFFFFFFFFFC2', rispettivamente (b / c del ciclo per carattere, credo).
Matthemattics,

1
Non funziona per me in Bash 4.3.11 (1). Jogging «à l'Hèze»Jogging%20%abà%20l%27Hèze%bbdecodeURIComponent
Viene

2
In quel primo blocco di codice cosa significa l'ultimo parametro da stampare? Cioè, perché si tratta di virgolette doppie, virgolette singole, segno del dollaro, lettera c, virgoletta doppia? Fa la virgoletta singola?
Colin Fraizer,

1
@dmcontador - è solo un modesto script bash, non ha il concetto di caratteri multi-byte o unicode. Quando vede un carattere come ń ( \u0144) genererà ingenuamente% 144, ╡ ( \u2561) verrà emesso come% 2561. Le risposte codificate in modo corretto per questi sarebbero rispettivamente% C5% 84% 0A e% E2% 95% A1.
Orwellophile,

1
@ColinFraizer la virgoletta singola serve per convertire il seguente carattere nel suo valore numerico. rif. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Sam

94

Usa il URI::Escapemodulo e la uri_escapefunzione di Perl nella seconda riga del tuo script bash:

...

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

Modifica: risolve i problemi di quotazione, come suggerito da Chris Johnsen nei commenti. Grazie!


2
URI :: Escape potrebbe non essere installato, controlla la mia risposta in quel caso.
azzurrato il

Ho risolto questo problema (use echo, pipe e <>), e ora funziona anche quando $ 2 contiene un apostrofo o virgolette doppie. Grazie!
Dubek,

9
Elimina echoanche:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen,

1
La versione di Chris Johnsen è migliore. Nella mia espressione di test avevo $ {True} e l'utilizzo di questo tramite echo ha provocato l'espansione della variabile uri_escape / Perl.
mm2001

1
@ jrw32982 sì, ripensandoci, avere un'altra lingua con cui svolgere questo compito è buono. Se potessi, riprenderei il mio
voto negativo

69

Un'altra opzione è usare jq(come filtro):

jq -sRr @uri

-R( --raw-input) considera le righe di input come stringhe invece di analizzarle come JSON e -sR( --slurp --raw-input) legge l'input in una singola stringa. -r( --raw-output) genera il contenuto delle stringhe anziché i valori letterali delle stringhe JSON.

Se l'input non è l'output di un altro comando, è possibile memorizzarlo in una jqvariabile stringa:

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

-n( --null-input) non legge l'input e --arg name valuememorizza valuein variabile namecome una stringa. Nel filtro $name(tra virgolette singole, per evitare l'espansione della shell), fa riferimento alla variabile name.

Avvolto come una funzione Bash, questo diventa:

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

O questa percentuale codifica tutti i byte:

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

3
<3 esso ... dovrebbe essere superiore e accettato IMO (sì, se si può dire curldi codificare che funziona e se bash ha un built-in che sarebbe stato accettabile - ma jqsembra una misura giusta per cui sono ben lungi dal raggiungere il livello di comfort con questo strumento)
nhed

5
per chiunque mi @urichieda la stessa cosa: non è una variabile, ma un filtro jq letterale usato per formattare stringhe e escape; vedere il manuale di jq per i dettagli (scusate, nessun collegamento diretto, è necessario cercare @urinella pagina ...)
ssc

la versione xxd è proprio il tipo di cosa che stavo cercando. Anche se è un po 'sporco, è corto e non ha dipendenze
Rian Sanderson,

1
Un esempio di utilizzo di jq per codificare l'URL:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal,

67

per completezza, molte soluzioni usano sedo awktraducono solo un set speciale di caratteri e sono quindi abbastanza grandi per dimensione del codice e non traducono anche altri caratteri speciali che dovrebbero essere codificati.

un modo sicuro per urlencode sarebbe semplicemente codificare ogni singolo byte, anche quelli che sarebbero stati autorizzati.

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

Qui xxd si preoccupa che l'input sia gestito come byte e non come caratteri.

modificare:

xxd viene fornito con il pacchetto vim-common in Debian ed ero solo su un sistema in cui non era installato e non volevo installarlo. L'altornativo è usare hexdumpdal pacchetto bsdmainutils in Debian. Secondo il grafico seguente, bsdmainutils e vim-common dovrebbero avere una probabilità circa uguale da installare:

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

ma tuttavia qui una versione che utilizza hexdumpinvece di xxde consente di evitare la trchiamata:

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

1
xxd -plaindovrebbe succedere DOPO tr -d '\n'!
qdii,

3
@qdii perché? ciò non solo renderebbe impossibile la codifica delle nuove righe, ma inserirà erroneamente anche le nuove righe create da xxd nell'output.
Josch

1
@josch. Questo è semplicemente sbagliato. Innanzitutto, tutti i \npersonaggi verranno tradotti xxd -plainin 0a. Non crederci sulla parola, provalo tu stesso: echo -n -e '\n' | xxd -plainquesto dimostra che tr -d '\n'qui sei inutile perché \ndopo xxd -plain Second non ce ne può essere , echo foobaraggiunge il proprio \ncarattere alla fine della stringa di caratteri, quindi xxd -plainnon si nutre foobarcome previsto ma con foobar\n. quindi lo xxd -plain traduce in una stringa di caratteri che termina 0a, rendendolo non adatto all'utente. Si potrebbe aggiungere -na echorisolverlo.
qdii,

6
@qdii infatti -n mancava per l'eco ma la xxdchiamata appartiene di fronte alla tr -dchiamata. Appartiene lì in modo che qualsiasi newline in foobarsia tradotto da xxd. Il tr -ddopo la xxdchiamata è di rimuovere le nuove righe prodotte da xxd. Sembra che tu non abbia mai il foobar abbastanza a lungo in modo da xxdprodurre nuove linee, ma per input lunghi lo farà. Quindi tr -dè necessario. Contrariamente a quanto si suppone, tr -dNON è stato necessario rimuovere le nuove righe dall'input ma xxddall'output. Voglio mantenere le newline nell'input. L'unico punto valido è che l'eco aggiunge una nuova riga non necessaria.
Josch

1
@qdii e senza offesa - penso solo che ti sbagli, tranne per quello echo -nche mi mancava davvero
josch

62

Una delle varianti, può essere brutta, ma semplice:

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
}

Ecco ad esempio la versione one-liner (come suggerito da 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
Penso che questo sia un modo molto intelligente per riutilizzare la codifica URL di cURL.
solidsnack,

13
Questo è assolutamente geniale! Vorrei davvero che tu avessi lasciato una riga in modo che la gente potesse vedere quanto fosse semplice. Per codificare l'URL del risultato del datecomando ... date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Devi cutdisattivare i primi 2 caratteri, perché l'output di curl è tecnicamente un URL relativo con una stringa di query.)
Bruno Bronosky

2
@BrunoBronosky La tua variante a una riga è buona ma sembra aggiungere un "% 0A" alla fine della codifica. Gli utenti attenti. La versione della funzione non sembra avere questo problema.
levigroker,

7
Per evitare %0Aalla fine, utilizzare printfinvece di echo.
Kenorb,

2
l'unica fodera è fantastica
Stephen Blum il

49

Lo trovo più leggibile in Python:

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

la tripla "assicura che le virgolette singole in valore non facciano male. urllib è nella libreria standard. Funziona come esempio per questo url pazzo (mondo reale):

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

2
Ho avuto qualche problema con le virgolette e i caratteri speciali con il triplequoting, questo sembrava funzionare praticamente per tutto: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. write (urllib.quote (sys.stdin.read ())) ")";
Smetti di calunniare Monica Cellio il

La versione di Python 3 sarebbe 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)'non ha quasi problemi di quotazione e dovrebbe essere efficiente in termini di memoria / velocità (non ho controllato, salvo per gli occhi
socchiusi

2
Sarebbe molto più sicuro fare riferimento sys.argvpiuttosto che sostituire $valuein una stringa successivamente analizzata come codice. E se valuecontenuto ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy,

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

30

Ho trovato utile il seguente frammento per inserirlo in una catena di chiamate di programma, in cui URI :: Escape potrebbe non essere installato:

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

( fonte )


4
ha funzionato per me. L'ho cambiato in perl -lpe ... (la lettera ell). Ciò ha rimosso la nuova riga finale, di cui avevo bisogno per i miei scopi.
JohnnyLambada,

2
Cordiali saluti, per fare il contrario , usa perl -pe 's/\%(\w\w)/chr hex $1/ge'(fonte: unix.stackexchange.com/questions/159253/… )
Sridhar Sarnobat,

2
A seconda di quali caratteri è necessario codificare, è possibile semplificare ciò perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'che consente lettere, numeri e caratteri di sottolineatura, ma codifica tutto il resto.
Robru

23

Se si desidera eseguire la GETrichiesta e utilizzare l'arricciatura pura, è sufficiente aggiungere--get la soluzione di @ Jacob.

Ecco un esempio:

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

15

Link diretto alla versione awk: http://www.shelldorado.com/scripts/cmds/urlencode L'
ho usato per anni e funziona come un fascino

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

Esiste una semplice variante per ottenere la codifica UTF-8 anziché ASCII?
avgvstvs,

15

Questo potrebbe essere il migliore:

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

Questo funziona per me con due aggiunte: 1. sostituisci -e con -n per evitare di aggiungere una nuova riga alla fine dell'argomento e 2. aggiungi '%%' alla stringa printf per mettere un% davanti a ciascuna coppia di cifre esadecimali.
Rob Fagen,

funziona dopo l'aggiunta di $ anticipazione after=$(echo -e ...
Roman Rhrn Nesterov

1
Per favore, spiega come funziona. Il odcomando non è comune.
Mark Stosberg,

Questo non funziona con OS X odperché utilizza un formato di output diverso da GNU od. Ad esempio, printf aa|od -An -tx1 -v|tr \ -stampa -----------61--61--------------------------------------------------------con OS X ode -61-61con GNU od. È possibile utilizzare od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\ncon OS X odo GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nfa la stessa cosa, anche se xxdnon è in POSIX ma lo odè.
nisetama,

2
Anche se questo potrebbe funzionare, sfugge a ogni singolo personaggio
Charlie,

11

Ecco una soluzione Bash che non invoca alcun programma esterno:

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
Questo si comporta diversamente tra le versioni bash. Su RHEL 6.9 la bash è 4.1.2 e include le virgolette singole. Mentre Debian 9 e bash 4.4.12 vanno bene con le virgolette singole. Per me rimuovere le virgolette singole ha funzionato su entrambi. s = "$ {s // ',' /% 2C}"
muni764,

1
Ho aggiornato la risposta per riflettere la tua scoperta, @ muni764.
davidchambers,

Solo un avvertimento ... questo non codificherà cose come il personaggioá
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')

questo codificherà la stringa all'interno di $ 1 e la produrrà in $ url. anche se non devi metterlo in un var se vuoi. BTW non includeva il sed per tab pensato che lo avrebbe trasformato in spazi


5
Ho la sensazione che questo non sia il modo consigliato per farlo.
Cody Grey

2
spiega il tuo sentimento per favore .... perché io quello che ho dichiarato funziona e l'ho usato in diversi script, quindi so che funziona per tutti i caratteri che ho elencato. quindi per favore spiega perché qualcuno non dovrebbe usare il mio codice e usare perl poiché il titolo di questo è "URLEncode da uno script bash" non uno script perl.
manoflinux,

a volte non è necessaria alcuna soluzione di perle, quindi può tornare utile
Yuval Rimar,

3
Questo non è il modo consigliato per farlo perché la blacklist è una cattiva pratica e comunque è unicode ostile.
Ekevoo,

Questa era la soluzione più amichevole compatibile con cat file.txt
mrwaim


7

Per quelli di voi che cercano una soluzione che non ha bisogno di perl, eccone una che ha bisogno solo di hexdump e 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
   '`
}

Cuciti insieme da un paio di punti attraverso la rete e alcuni tentativi ed errori locali. Funziona benissimo!


7

uni2ascii è molto utile:

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

2
Questo non funziona per i personaggi all'interno dell'intervallo ASCII, che necessitano di virgolette, like %e spazio (l'ultimo che può essere risolto con la -sbandiera)
Boldewyn

7

Se non vuoi dipendere da Perl puoi anche usare sed. È un po 'disordinato, poiché ogni personaggio deve essere evaso individualmente. Crea un file con i seguenti contenuti e chiamalourlencode.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

Per usarlo, procedi come segue.

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"

Ciò dividerà la stringa in una parte che necessita di codifica e la parte corretta, codificherà la parte che ne ha bisogno, quindi ricucirà insieme.

Puoi metterlo in uno script sh per comodità, magari avere un parametro da codificare, metterlo sul tuo percorso e quindi puoi semplicemente chiamare:

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

fonte


7

Puoi emulare javascript encodeURIComponentin perl. Ecco il comando:

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

È possibile impostare questo come alias bash in .bash_profile:

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

Ora puoi eseguire il pipe in encodeURIComponent:

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

6

Ecco la versione del nodo:

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

1
Non si interromperà se ci sono altri caratteri nella stringa che non sono validi tra virgolette singole, come una singola barra rovesciata o nuove righe?
Stuart P. Bentley,

Buon punto. Se dovessimo sfuggire a tutti i personaggi problematici di Bash, potremmo anche eseguire direttamente i rimpiazzi ed evitare del nodetutto. Ho pubblicato una soluzione solo Bash. :)
davidchambers

1
Questa variante trovata altrove nella pagina evita il problema delle citazioni leggendo il valore da STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg

6

La domanda è di farlo in bash e non c'è bisogno di Python o Perl in quanto esiste in realtà un singolo comando che fa esattamente quello che vuoi - "urlencode".

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

Anche questo è molto meglio, poiché la risposta perl sopra, ad esempio, non codifica correttamente tutti i caratteri. Provalo con il trattino lungo che ottieni da Word e ottieni la codifica sbagliata.

Nota, per fornire questo comando devi installare "client-gridsite".


1
La mia versione di bash (GNU 3.2) non ha urlencode. Quale versione stai usando?
Sridhar Sarnobat,

1
Ho 4.3.42, ma il comando urlencode è fornito da "gridsite-client". Prova a installarlo e dovresti andare bene.
Dylan,

5
Quindi la tua risposta non è migliore di quella che richiede l'installazione di altre cose (python, perl, lua, ...)
Cyrille Pontvieux,

Solo che richiede solo l'installazione di una singola utility anziché di un intero linguaggio (e librerie), inoltre è super semplice e chiaro per vedere cosa sta facendo.
Dylan,

Un collegamento prima per la pagina del pacchetto / progetto che fornisce questo comando sarebbe stato utile.
Doron Behar,

6

Semplice opzione PHP:

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

4

Ruby, per completezza

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

4

Un altro approccio php:

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

2
echoaggiungerà un carattere di nuova riga (esadecimale 0xa). Per impedirlo, usa echo -n.
Mathew Hall,

3

Ecco la mia versione per la shell ash di busybox per un sistema incorporato, originariamente ho adottato la variante di 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

Ecco una funzione POSIX per farlo:

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

Esempio:

value=$(encodeURIComponent "$2")

fonte


2

Ecco una conversione di una riga usando Lua, simile alla risposta di blueyed ad eccezione di tutti i caratteri non prenotati RFC 3986 lasciati non codificati (come questa risposta ):

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

Inoltre, potrebbe essere necessario assicurarsi che le nuove righe nella stringa vengano convertite da LF a CRLF, nel qual caso è possibile inserire un gsub("\r?\n", "\r\n")nella catena prima della codifica percentuale.

Ecco una variante che, nello stile non standard di application / x-www-form-urlencoded , fa quella normalizzazione della nuova riga, così come codifica gli spazi come '+' invece di '% 20' (che potrebbe probabilmente essere aggiunto al Frammento di Perl usando una tecnica simile).

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

1

Dopo aver installato php, utilizzo in questo modo:

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

1

Questa è la versione ksh della risposta di orwellophile contenente le funzioni rawurlencode e rawurldecode (link: Come urlencode i dati per il comando curl? ). Non ho abbastanza rappresentante per pubblicare un commento, quindi il nuovo post ..

#!/bin/ksh93

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

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

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

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

1

Cosa analizzerebbe gli URL meglio di javascript?

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

Portata fuori discussione. Non colpire, non arricciare. Anche se sono sicuro che funziona molto bene se il nodo è disponibile.
Cyrille Pontvieux,

Perché sottovalutare questo e non le risposte python / perl? Inoltre, come questo non risponde alla domanda originale "Come urlencode i dati per il comando curl?". Questo può essere usato da uno script bash e il risultato può essere dato a un comando curl.
Nestor Urquiza,

Ho votato in negativo anche gli altri. La domanda era come farlo in uno script bash. Se viene utilizzata un'altra lingua come node / js, python o perl, non è necessario utilizzare direttamente l'arricciatura.
Cyrille Pontvieux,

2
Anche se non mi sono preoccupato di effettuare il downvote, il problema con questo comando è che richiede che i dati siano salvati correttamente per l'uso in JavaScript. Come provarlo con virgolette singole e un po 'di follia. Se vuoi usare il nodo, è meglio leggere cose da node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Stdin

1
Fai attenzione con la soluzione di @ MichaelKrelin-hacker se esegui il piping dei dati da STDIN assicurati di non includere una nuova riga finale. Ad esempio, echo | ...è sbagliato, mentre echo -n | ...sopprime la nuova riga.
Mark Stosberg,

0

Quanto segue si basa sulla risposta di Orwellophile, ma risolve il bug multibyte menzionato nei commenti impostando LC_ALL = C (un trucco di vte.sh). L'ho scritto sotto forma di funzione adatta PROMPT_COMMAND, perché è così che 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}"
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.