Come urlencode i dati per il comando di arricciatura?

Sto provando a scrivere uno script bash per i test che accetta un parametro e lo invia tramite curl al sito web. Ho bisogno di 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 [email protected] 

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

Questo invia dati, simili alle altre opzioni --data con l’eccezione che questo esegue la codifica dell’URL. Per essere conforms a CGI, la parte dovrebbe iniziare con un nome seguito da un separatore e 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 il ricciolo 7.18.0 o successivo (rilasciato a gennaio 2008) . Usa curl -V per verificare quale versione hai.

Ecco la pura risposta di BASH.

 rawurlencode() { local string="${1}" local strlen=${#string} local encoded="" local pos co for (( pos=0 ; pos 

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 corrispondente, ora possiamo eseguire alcuni semplici test:

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

E se davvero senti che hai bisogno di uno strumento esterno (beh, andrà molto più veloce, e potrebbe fare file binari e così via ...) 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 

Usa il modulo URI::Escape di Perl e la funzione uri_escape nella seconda riga del tuo script bash:

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

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

Per completezza, molte soluzioni che usano sed o awk traducono solo un set speciale di caratteri e sono quindi abbastanza grandi per dimensione del codice e inoltre non traducono altri caratteri speciali che dovrebbero essere codificati.

un modo sicuro per urlencode sarebbe quello di codificare ogni singolo byte, anche quelli che sarebbero stati consentiti.

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

xxd sta facendo attenzione che l’input sia gestito come byte e non come caratteri.

modificare:

xxd viene fornito con il pacchetto vim-common in Debian e io ero su un sistema in cui non era installato e non volevo installarlo. L’alternativa è usare hexdump dal pacchetto bsdmainutils in Debian. Secondo il seguente grafico, bsdmainutils e vim-common dovrebbero avere una probabilità circa uguale di essere installati:

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

ma comunque qui una versione che usa hexdump invece di xxd e permette di evitare la chiamata tr :

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

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 danneggino. urllib è nella libreria standard. Funziona come esempio per questo pazzo (mondo reale) url:

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

Una delle varianti potrebbe 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 la versione one-liner ad esempio (come suggerito da Bruno ):

 date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3- 

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

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

( fonte )

Se desideri eseguire la richiesta GET e utilizzare il curl puro, aggiungi --get @ alla soluzione di Jacob.

Ecco un esempio:

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

Un’altra opzione è usare jq :

 jq -s -R -r @uri 

-s ( --slurp ) legge le linee di input in una matrice e -s -R ( --slurp --raw-input ) legge l’input in una singola stringa. -r ( --raw-output ) restituisce il contenuto delle stringhe anziché i valori letterali stringa JSON.

O questa percentuale-codifica tutti i byte:

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

Link diretto alla versione di 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 ([email protected]) # Date : 2000-03-15 # Requires : awk # Categories : File Conversion, WWW, CGI # SCCS-Id. : @(#) urlencode 1.4 06/10/29 ########################################################################## # Description # Encode data according to # RFC 1738: "Uniform Resource Locators (URL)" and # RFC 1866: "Hypertext Markup Language - 2.0" (HTML) # # This encoding is used ie 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 "[email protected]"; exit 1; } set -- `getopt hl "[email protected]" 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 ABCDEF", 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 "" } ' "[email protected]" 

Questo potrebbe essere il migliore:

 after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s") 
 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 una var se vuoi. BTW non ha incluso il sed per tab pensato che lo trasformsrebbe in spazi

Per quelli di voi che cercano una soluzione che non ha bisogno di perl, eccone uno che ha solo bisogno 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 posti in rete e alcuni tentativi ed errori locali. Funziona alla grande!

Usando php da uno script di shell:

 value="http://www.google.com" encoded=$(php -r "echo rawurlencode('$value');") # encoded = "http%3A%2F%2Fwww.google.com" echo $(php -r "echo rawurldecode('$encoded');") # returns: "http://www.google.com" 
  1. http://www.php.net/manual/en/function.rawurlencode.php
  2. http://www.php.net/manual/en/function.rawurldecode.php

uni2ascii è molto utile:

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

Se non vuoi dipendere da Perl puoi anche usare sed. È un po ‘caotico, dato che ogni personaggio deve essere scappato individualmente. Crea un file con il seguente contenuto e chiamalo urlencode.sed

 s/%/%25/g s/ /%20/g s/ /%09/g s/!/%21/g s/"/%22/g s/#/%23/g s/\$/%24/g s/\&/%26/g s/'\''/%27/g s/(/%28/g s/)/%29/g s/\*/%2a/g s/+/%2b/g s/,/%2c/g s/-/%2d/g s/\./%2e/g s/\//%2f/g s/:/%3a/g s/;/%3b/g s//%3e/g s/?/%3f/g s/@/%40/g s/\[/%5b/g s/\\/%5c/g s/\]/%5d/g s/\^/%5e/g s/_/%5f/g s/`/%60/g s/{/%7b/g s/|/%7c/g s/}/%7d/g s/~/%7e/g s/ /%09/g 

Per usarlo, fai quanto segue.

 STR1=$(echo "https://www.example.com/change&$ ^this to?%[email protected]" | cut -d\? -f1) STR2=$(echo "https://www.example.com/change&$ ^this to?%[email protected]" | cut -d\? -f2) OUT2=$(echo "$STR2" | sed -f urlencode.sed) echo "$STR1?$OUT2" 

Questo dividerà la stringa in una parte che richiede la codifica, e la parte che va bene, codificherà la parte che ne ha bisogno, quindi ricomincerà a cucire insieme.

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

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

fonte

La domanda è di fare questo in bash e non c’è bisogno di python o perl dato che c’è in effetti un singolo comando che fa esattamente quello che vuoi – “urlencode”.

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

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

Nota, hai bisogno di “gridsite-clients” installati per fornire questo comando.

È ansible emulare il codecomponent di javascript in perl. Ecco il comando:

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

Puoi impostare questo come un alias di bash in .bash_profile :

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

Ora puoi inserire pipe in encodeURIComponent :

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

Opzione PHP semplice:

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

Un altro approccio php:

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

Ecco la versione del nodo:

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

Rubino, per completezza

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

Ecco una soluzione Bash che non richiama nessun 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" } 

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()-]/ ? qy : q sprintf("%%%02X", z[y]) print q}' "$1" } 

Esempio:

 value=$(encodeURIComponent "$2") 

fonte

Ecco una conversione di una riga usando Lua, simile alla risposta di blueyed, tranne con tutti i caratteri non riservati 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 i newline nella stringa vengano convertiti da LF a CRLF, nel qual caso è ansible inserire un gsub("\r?\n", "\r\n") nella catena prima della percentuale- codifica.

Ecco una variante che, nello stile non standard di application / x-www-form-urlencoded , esegue la normalizzazione della newline, oltre agli spazi di codifica 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") 

Avendo installato php uso in questo modo:

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

Questa è la versione ksh della risposta di orwellophile che contiene le funzioni rawurlencode e rawurldecode (link: come urlencode data per il comando curl? ). Non ho abbastanza rep per pubblicare un commento, quindi il nuovo post ..

 #!/bin/ksh93 function rawurlencode { typeset string="${1}" typeset strlen=${#string} typeset encoded="" for (( pos=0 ; pos C%2b%2b print $(rawurldecode "C%2b%2b") # --> C++ 

Ecco la mia versione per busybox ash shell per un sistema embedded, ho originariamente 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  local url_encoded="${1//+/ }" printf '%b' "${url_encoded//%/\\x}" } 

Quanto segue si basa sulla risposta di Orwellophile, ma risolve il bug multibyte menzionato nei commenti impostando LC_ALL = C (un trucco da vte.sh). L’ho scritto sotto forma di funzione PROMPT_COMMAND adatta, perché è così che lo uso.

 print_path_url() { local LC_ALL=C local string="$PWD" local strlen=${#string} local encoded="" local pos co for (( pos=0 ; pos 

Cosa analizzerebbe meglio gli URL di javascript?

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