Domanda

Sto provando a scrivere uno script bash per 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 $@
È stato utile?

Soluzione

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

  

Questo pubblica dati, simili alle altre --data opzioni con l'eccezione che questo esegue la codifica URL. Per essere conforme a CGI, la parte <data> dovrebbe iniziare con un nome seguito da un separatore e una specifica di 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 curl 7.18.0 o più recente (rilasciato a gennaio 2008) . Usa curl -V per verificare quale versione hai.

Altri suggerimenti

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}

[a cura]

Ecco la funzione rawurldecode () corrispondente, 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

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: risolti i problemi di quotazione, 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 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.

modifica:

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. Il punto di partenza è usare hexdump dal 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 <> amp;!! show_installed = 1 <> amp;! want_legend = 1 <> amp; want_ticks = 1

ma tuttavia qui una versione che utilizza xxd anziché tr e consente di evitare la <=> chiamata:

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

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 la versione one-liner per esempio (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/'

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

Un'altra opzione è usare jq:

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 contiene feed di linea (o non si desidera sfuggirli come %0A), è possibile utilizzare solo jq -Rr @uri senza l'opzione -s.

O questa percentuale codifica tutti i byte:

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

Ho trovato utile il seguente frammento 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'

( source )

Se desideri eseguire GET richiesta e utilizzare il puro ricciolo, 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

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

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 un var se vuoi. BTW non includeva il sed per tab pensato che lo avrebbe trasformato in spazi

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

Per quelli di voi che cercano una soluzione che non ha bisogno di perl, eccone una 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 punti attraverso la rete e alcuni tentativi ed errori locali. Funziona benissimo!

Uso di 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 'disordinato, poiché ogni personaggio deve essere evaso individualmente. Crea un file con i seguenti contenuti 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, 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"

Questo dividerà la stringa in una parte che ha bisogno di essere codificata e la parte che va bene, 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 poi puoi semplicemente chiamare:

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

source

Puoi emulare encodeURIComponent in perl. Ecco il comando:

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

Puoi impostarlo 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 <=>:

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

Ecco la versione del nodo:

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

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, è necessario " gridsite-client " installato per fornire questo comando.

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

Ruby, per completezza

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

Ecco la mia versione per la shell ash di busybox per un sistema incorporato, inizialmente 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}"
}

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

Source

Ecco una conversione di una riga usando Lua, simile a risposta di blueyed ad eccezione di tutti i RFC 3986 caratteri non prenotati 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, in lo stile non standard dell'applicazione / x -www-form-urlencoded , esegue questa nuova normalizzazione, oltre a codificare gli spazi come '+' invece di '% 20' (che probabilmente potrebbe essere aggiunto allo snippet del 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")

Dopo aver installato php, utilizzo in questo modo:

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

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

Cosa analizzerebbe gli URL meglio di javascript?

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

Quanto segue si basa sulla risposta di Orwellophile, ma risolve il multibyte bug 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}"
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top