538 Stimmen

Wie zu urlencode Daten für curl Befehl?

Ich versuche, ein Bash-Skript zum Testen zu schreiben, das einen Parameter nimmt und ihn über curl an eine Website sendet. Ich muss den Wert url-codieren, um sicherzustellen, dass Sonderzeichen richtig verarbeitet werden. Was ist der beste Weg, dies zu tun?

Hier ist mein bisheriges Grundskript:

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

659voto

Jacob Rask Punkte 19740

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

Diese Daten werden, ähnlich wie bei den anderen --data Optionen, mit der Ausnahme, dass diese die URL-Kodierung durchführt. Um CGI-kompatibel zu sein, muss die <data> Teil sollte mit einem Namen beginnen, gefolgt von einem Trennzeichen und einer Inhaltsangabe.

Beispiel für die Verwendung:

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 Sie curl -V um zu prüfen, welche Version Sie haben.

Sie können auch die Kodierung der Abfragezeichenfolge :

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

314voto

nisetama Punkte 5907

Eine weitere Möglichkeit ist die Verwendung von jq :

$ printf %s 'encode this'|jq -sRr @uri
encode%20this
$ jq -rn --arg x 'encode this' '$x|@uri'
encode%20this

-r ( --raw-output ) gibt die rohen Inhalte von Strings anstelle von JSON-Stringliteralen aus. -n ( --null-input ) liest keine Eingaben von STDIN.

-R ( --raw-input ) behandelt Eingabezeilen als Strings, anstatt sie als JSON zu parsen, und -sR ( --slurp --raw-input ) liest die Eingabe in eine einzige Zeichenkette. Sie können ersetzen -sRr con -Rr wenn Ihre Eingabe nur eine einzige Zeile enthält, oder wenn Sie Zeilenumbrüche nicht durch %0A :

$ printf %s\\n 'multiple lines' 'of text'|jq -Rr @uri
multiple%20lines
of%20text
$ printf %s\\n 'multiple lines' 'of text'|jq -sRr @uri
multiple%20lines%0Aof%20text%0A

Oder diese Prozentkodierung verschlüsselt alle Bytes:

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

237voto

Orwellophile Punkte 12151

Hier ist die reine BASH-Antwort.

Update : Da viele Änderungen diskutiert wurden, habe ich dies auf https://github.com/sfinktah/bash/blob/master/rawurlencode.inc.sh gegen die niemand eine PR herausgeben kann.

Hinweis : Diese Lösung ist nicht dafür gedacht, Unicode- oder Multi-Byte-Zeichen zu kodieren - diese liegen außerhalb der bescheidenen Möglichkeiten von BASH einheimische Fähigkeiten. Es ist nur dazu gedacht, Symbole zu kodieren, die sonst die Argumentübergabe in POST- oder GET-Anfragen ruinieren würden, z. B. '&', '=' und so weiter.

Sehr wichtiger Hinweis : VERSUCHEN SIE NICHT, IHRE EIGENE UNICODE-KONVERTIERUNGSFUNKTION ZU SCHREIBEN, IN KEINER SPRACHE, NIEMALS. Siehe Ende der 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 Matching-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 brauchen (nun ja, es geht viel schneller und kann auch Binärdateien und dergleichen...), 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 diese 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

Es ist zwar nicht unmöglich, ein solches Skript in BASH zu schreiben (wahrscheinlich mit xxd und ein sehr umfangreicher Regelsatz), die UTF-8-Eingaben verarbeiten können, gibt es schnellere und zuverlässigere Methoden. Der Versuch, UTF-8 in UTF-32 zu dekodieren, ist eine nicht-triviale Aufgabe, die mit Genauigkeit zu erledigen ist, obwohl es sehr einfach ist, sie ungenau zu erledigen, so dass man denkt, es funktioniert, bis zu dem Tag, an dem es nicht mehr funktioniert.

Selbst das Unicode-Konsortium hat seinen Beispielcode entfernt, nachdem es festgestellt hatte, dass er nicht mehr zu 100 % mit dem aktuellen Standard kompatibel war.

Der Unicode-Standard wird ständig weiterentwickelt und ist sehr differenziert geworden. Jede Implementierung, die Sie zusammenbasteln können, wird nicht richtig konform sein, und selbst wenn Sie es mit extremem Aufwand schaffen, würde es bleiben willfährig.

106voto

dubek Punkte 10669

Verwenden Sie Perl's 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")"
...

編集する。 Behebung von Zitierproblemen, wie von Chris Johnsen in den Kommentaren vorgeschlagen. Danke!

82voto

Sergey Punkte 799

Eine der Varianten, die zwar hässlich, aber einfach ist:

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 Einzeiler-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/'

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X