it-swarm.com.de

Wie werden Daten für den Curl-Befehl urlencodiert?

Ich versuche, ein Bash-Skript zum Testen zu schreiben, das einen Parameter aufnimmt und ihn per Curl an die Website sendet. Ich muss den Wert url-codieren, um sicherzustellen, dass Sonderzeichen ordnungsgemäß verarbeitet werden. Wie geht das am besten?

Hier ist mein Grundskript:

#!/bin/bash
Host=${1:?'bad Host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${Host}/somepath [email protected]
286
Aaron

Verwenden curl --data-urlencode; von man curl:

Hier werden Daten ähnlich wie bei den anderen --data -Optionen mit der Ausnahme, dass hierdurch eine URL-Codierung durchgeführt wird. Um CGI-konform zu sein, muss <data> Teil sollte mit einem Namen beginnen, gefolgt von einem Trennzeichen und einer Inhaltsangabe.

Anwendungsbeispiel:

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

Siehe die Manpage für weitere Informationen.

Dies erfordert curl 7.18.0 oder neuer (veröffentlicht im Januar 2008) . Verwenden curl -V, um zu überprüfen, welche Version Sie haben.

344
Jacob R

Hier ist die reine BASH-Antwort.

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
}

Sie können es auf zwei Arten verwenden:

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

[bearbeitet]

Hier ist die passende Funktion rawurldecode (), die - bei aller Bescheidenheit - großartig ist.

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

Mit dem passenden Set können wir nun einige einfache Tests durchführen:

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

Output: Matched

Und wenn Sie wirklich das Gefühl haben, dass Sie ein externes Tool benötigen (nun, es wird viel schneller gehen und möglicherweise Binärdateien und so weiter ausführen ...), habe ich dies auf meinem OpenWRT-Router gefunden ...

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

Dabei war url_escape.sed eine Datei, die die folgenden Regeln enthielt:

# 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
161
Orwellophile

Benutze Perls URI::Escape Modul und uri_escape Funktion in der zweiten Zeile Ihres Bash-Skripts:

...

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

Edit: Behebung von Zitierproblemen, wie von Chris Johnsen in den Kommentaren vorgeschlagen. Vielen Dank!

90
dubek

der Vollständigkeit halber übersetzen viele Lösungen, die sed oder awk verwenden, nur einen speziellen Zeichensatz und sind daher von der Codegröße her ziemlich groß und übersetzen auch keine anderen Sonderzeichen, die codiert werden sollten.

ein sicherer Weg zum Urlencode wäre, einfach jedes einzelne Byte zu verschlüsseln - auch die, die erlaubt gewesen wären.

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

xxd achtet hierbei darauf, dass die Eingabe als Byte und nicht als Zeichen behandelt wird.

bearbeiten:

xxd wird mit dem vim-common-Paket in Debian geliefert und ich befand mich gerade auf einem System, auf dem es nicht installiert war und das ich nicht installieren wollte. Die Alternative ist, hexdump aus dem Paket bsdmainutils in Debian zu verwenden. Nach dem folgenden Diagramm sollten bsdmainutils und vim-common mit etwa gleicher Wahrscheinlichkeit installiert werden:

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

aber dennoch hier eine Version, die hexdump anstelle von xxd verwendet und den tr -Aufruf vermeidet:

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

Eine der Varianten kann hässlich, aber einfach sein:

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
}

Hier ist zum Beispiel die einzeilige Version (wie vorgeschlagen von 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/'
49
Sergey

Ich finde es besser lesbar in Python:

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

das Triple 'sorgt dafür, dass einfache Anführungszeichen nicht schaden. urllib befindet sich in der Standardbibliothek. Es funktioniert zum Beispiel für diese verrückte (reale) URL:

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

Eine andere Option ist die Verwendung von jq:

jq -sRr @uri

-R (--raw-input) Behandelt Eingabezeilen als Zeichenfolgen, anstatt sie als JSON zu analysieren, und -sR (--Slurp --raw-input) Liest die Eingabe in eine einzelne Zeichenfolge. -r (--raw-output) Gibt den Inhalt von Zeichenfolgen anstelle von JSON-Zeichenfolgenliteralen aus.

Wenn die Eingabe keine Zeilenvorschübe enthält (oder Sie sie nicht als %0A Maskieren möchten), können Sie nur jq -Rr @uri Ohne die Option -s Verwenden.

Oder dieser Prozentsatz codiert alle Bytes:

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

Ich fand das folgende Snippet nützlich, um es in eine Reihe von Programmaufrufen einzufügen, in denen URI :: Escape möglicherweise nicht installiert ist:

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

( Quelle )

29
blueyed

Wenn Sie GET request ausführen und pure curl verwenden möchten, fügen Sie einfach --get zu @ Jacobs Lösung.

Hier ist ein Beispiel:

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

Das könnte das Beste sein:

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

Direkter Link zur awk-Version: http://www.shelldorado.com/scripts/cmds/urlencode
Ich habe es jahrelang benutzt und es funktioniert wie ein Zauber

:
##########################################################################
# 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 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 "[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 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 ""
    }
' "[email protected]"
14
MatthieuP
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')

dadurch wird der String in $ 1 kodiert und in $ url ausgegeben. obwohl Sie es nicht in eine Variable setzen müssen, wenn Sie wollen. Übrigens enthielt das sed for tab nicht, da es in Leerzeichen umgewandelt werden würde

10
manoflinux

Hier ist eine Bash-Lösung, die keine externen Programme aufruft:

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

Für diejenigen unter Ihnen, die nach einer Lösung suchen, die Perl nicht benötigt, ist hier eine, die nur hexdump und awk benötigt:

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
   '`
}

Von ein paar Stellen im Netz zusammengenäht und mit ein paar lokalen Versuchen und Irrtümern. Es funktioniert super

7
Louis Marascio

ni2ascii ist sehr praktisch:

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

Sie können Javascript encodeURIComponent in Perl emulieren. Hier ist der Befehl:

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

Sie können dies als Bash-Alias ​​in .bash_profile Festlegen:

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

Jetzt können Sie in encodeURIComponent leiten:

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

Wenn Sie nicht auf Perl angewiesen sind, können Sie auch sed verwenden. Es ist ein bisschen chaotisch, da jeder Charakter einzeln entkommen muss. Erstelle eine Datei mit folgendem Inhalt und nenne sie 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

Um es zu benutzen, gehen Sie wie folgt vor.

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"

Dadurch wird die Zeichenfolge in einen Teil aufgeteilt, der codiert werden muss, und der Teil, der in Ordnung ist, codiert den Teil, der es benötigt, und stickt dann wieder zusammen.

Sie können das der Einfachheit halber in ein sh-Skript einfügen. Vielleicht müssen Sie einen Parameter zum Codieren verwenden, ihn auf Ihren Pfad setzen und dann einfach Folgendes aufrufen:

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

Quelle

6
Jay

Verwenden von PHP aus einem Shell-Skript:

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
6
Darren Weber

Bei der Frage geht es darum, dies in Bash zu tun, und es besteht keine Notwendigkeit für python oder Perl, da es tatsächlich einen einzigen Befehl gibt, der genau das tut, was Sie wollen - "urlencode".

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

Dies ist auch viel besser, da die obige Perl-Antwort beispielsweise nicht alle Zeichen korrekt codiert. Probieren Sie es mit dem langen Gedankenstrich aus, den Sie von Word erhalten, und Sie erhalten die falsche Codierung.

Beachten Sie, dass Sie "gridsite-clients" installiert haben müssen, um diesen Befehl bereitzustellen.

5
Dylan

Einfache PHP Option:

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

Hier ist die Knotenversion:

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

Ein anderer PHP-Ansatz:

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

Ruby, der Vollständigkeit halber

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

Hier ist meine Version für busybox ash Shell für ein Embedded-System, ich habe ursprünglich die Variante von Orwellophile übernommen:

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

Hier ist eine einzeilige Konvertierung mit Lua, ähnlich wie blueyeds Antwort mit Ausnahme aller RFC 3986 nicht reservierten Zeichen unverschlüsselt gelassen (wie diese Antwort ):

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

Außerdem müssen Sie möglicherweise sicherstellen, dass die Zeilenumbrüche in Ihrer Zeichenfolge von LF in CRLF konvertiert werden. In diesem Fall können Sie vor der Prozentcodierung eine gsub("\r?\n", "\r\n") in die Kette einfügen .

Hier ist eine Variante, die in dem nicht standardmäßigen Anwendungsstil/x-www-form-urlencoded diese Zeilenumbruchnormalisierung ausführt und Leerzeichen als '+' anstelle von '% 20' codiert ( Dies könnte wahrscheinlich mit einer ähnlichen Technik zum Perl-Snippet hinzugefügt werden.

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

Hier ist eine POSIX-Funktion, um dies zu tun:

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

Beispiel:

value=$(encodeURIComponent "$2")

Quelle

2
Steven Penny

Dies ist die ksh-Version der Antwort von orwellophile, die die Funktionen rawurlencode und rawurldecode enthält (Link: Wie werden Daten für den Befehl curl urlencode? ). Ich habe nicht genug Repräsentanten, um einen Kommentar zu schreiben, daher der neue Beitrag.

#!/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

Wenn ich PHP installiert habe, benutze ich diesen Weg:

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

Was würde URLs besser analysieren als Javascript?

node -p "encodeURIComponent('$url')"
0
Nestor Urquiza

Das Folgende basiert auf der Antwort von Orwellophile, behebt jedoch den in den Kommentaren erwähnten Multibyte-Fehler durch Setzen von LC_ALL = C (ein Trick von vte.sh). Ich habe es in der für Prompt_COMMAND geeigneten Funktionsform geschrieben, weil ich es so benutze.

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}"
}
0
Per Bothner