Wie Urlencode-Daten für Curl-Befehl?

319

Ich versuche, ein Bash-Skript zum Testen zu schreiben, das einen Parameter verwendet und ihn über Curl an die Website sendet. Ich muss den Wert per URL codieren, um sicherzustellen, dass Sonderzeichen ordnungsgemäß verarbeitet werden. Was ist der beste Weg, dies zu tun?

Hier ist mein bisheriges Basisskript:

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

Antworten:

393

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

Dadurch werden Daten veröffentlicht, ähnlich wie bei den anderen --dataOptionen, mit der Ausnahme, dass hierdurch eine URL-Codierung durchgeführt wird. Um CGI-kompatibel zu sein, sollte das <data>Teil mit einem Namen beginnen, gefolgt von einem Trennzeichen und einer Inhaltsspezifikation.

Anwendungsbeispiel:

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

Weitere Informationen finden Sie auf der Manpage .

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

Sie können auch die Abfragezeichenfolge codieren :

curl -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202
Jacob Rask
quelle
5
Scheint nur für http POST zu funktionieren. Dokumentation hier: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James
82
@StanJames Wenn Sie es so verwenden, kann Curl auch die Codierung für eine GET-Anforderung durchführen. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
Kberg
13
@kberg eigentlich funktioniert dies nur für Abfragedaten. Curl wird ein '?' gefolgt von den urlencodierten Parametern. Wenn Sie einen URL-Postfix urlencodieren möchten (z. B. ein CouchDB GET für eine Dokument-ID), funktioniert '--data-urlencode' nicht.
Bokeh
1
Funktioniert nicht für curl --data-urlencode "description=![image]($url)" www.example.com. Irgendeine Idee warum? `
Khurshid Alam
1
@NadavB Escaping the "
Black Jack
179

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 - fantastisch 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 wirklich das Gefühl haben, dass Sie ein externes Tool benötigen (nun, es wird viel schneller gehen und möglicherweise Binärdateien und dergleichen ausführen ...), habe ich dies auf meinem OpenWRT-Router gefunden ...

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

Wobei url_escape.sed eine Datei war, 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
Orwellophil
quelle
4
Leider schlägt dieses Skript bei einigen Zeichen fehl, z. B. 'é' und '½', und gibt 'e% FFFFFFFFFFFFFFCC' bzw. '% FFFFFFFFFFFFFFC2' aus (b / c der Zeichenschleife, glaube ich).
Matthemattics
1
In Bash 4.3.11 (1) funktioniert es bei mir nicht. Die Zeichenfolge Jogging «à l'Hèze»generiert Jogging%20%abà%20l%27Hèze%bb, die nicht an JS weitergeleitet werden kann decodeURIComponent:(
dmcontador
2
Was bedeutet der letzte zu druckende Parameter in diesem ersten Codeblock? Das heißt, warum ist es doppeltes Anführungszeichen, einfaches Anführungszeichen, Dollarzeichen, Buchstabe c, doppeltes Anführungszeichen? Funktioniert das einfache Anführungszeichen?
Colin Fraizer
1
@dmcontador - es ist nur ein bescheidenes Bash-Skript, es hat keine Vorstellung von Multi-Byte-Zeichen oder Unicode. Wenn es ein Zeichen wie ń ( \u0144) sieht, wird es naiv% 144 ausgeben, ╡ ( \u2561) wird als% 2561 ausgegeben. Die richtigen rohurlencodierten Antworten für diese wären% C5% 84% 0A bzw.% E2% 95% A1.
Orwellophile
1
@ColinFraizer Das einfache Anführungszeichen dient dazu, das folgende Zeichen in seinen numerischen Wert umzuwandeln. ref. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Sam
94

Verwenden Sie Perls URI::EscapeModul und uri_escapeFunktion in der zweiten Zeile Ihres Bash-Skripts:

...

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

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

Dubek
quelle
2
URI :: Escape ist möglicherweise nicht installiert. Überprüfen Sie in diesem Fall meine Antwort.
Blueyed
Ich habe dies behoben (use echo, pipe und <>), und jetzt funktioniert es auch dann, wenn $ 2 ein Apostroph oder doppelte Anführungszeichen enthält. Vielen Dank!
Dubek
9
Sie beseitigen echoauch:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen
1
Chris Johnsens Version ist besser. Ich hatte $ {True} in meinem Testausdruck und die Verwendung dieses Echos über das Echo löste die Variablenerweiterung uri_escape / Perl aus.
mm2001
1
@ jrw32982 Ja, wenn ich zurückblicke, ist es gut, eine andere Sprache zu haben, mit der ich diese Aufgabe erfüllen kann. Wenn ich könnte, würde ich meine Ablehnung zurücknehmen, aber leider ist sie derzeit gesperrt.
Thecoshman
69

Eine andere Option ist die Verwendung jq(als Filter):

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 nicht die Ausgabe eines anderen Befehls ist, können Sie sie in a speichern jq Zeichenfolgenvariablen :

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

-n( --null-input) liest keine Eingaben und --arg name valuespeichert sievalue Variable nameals Zeichenfolge. $nameVerweist im Filter (in einfachen Anführungszeichen, um eine Erweiterung durch die Shell zu vermeiden) auf die Variable name.

Als Bash-Funktion verpackt, wird dies:

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

Oder dieser Prozentsatz codiert alle Bytes:

xxd -p|tr -d \\n|sed 's/../%&/g'
Nisetama
quelle
3
<3 es ... sollte top & akzeptiert sein IMO (ja, wenn Sie sagen können, dass curldie Codierung funktioniert, und wenn bash eine integrierte Funktion hat, die akzeptabel gewesen wäre - aber es jqscheint eine richtige Passform zu sein, mit der ich weit davon entfernt bin, das Komfortniveau zu erreichen dieses Tool)
nhed
5
für alle, die sich das Gleiche fragen wie ich: @uriist keine Variable, sondern ein Literal-JQ-Filter, der zum Formatieren von Strings und zum Escapezeichen verwendet wird; Siehe jq Handbuch für Details (sorry, kein direkter Link, muss @uriauf der Seite
gesucht werden
Die xxd-Version ist genau das, wonach ich gesucht habe. Auch wenn es ein wenig schmutzig ist, ist es kurz und hat keine Abhängigkeiten
Rian Sanderson
1
Ein Beispiel für die Verwendung von jq zum URL-Codieren:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal
67

der Vollständigkeit halber viele Lösungen mit sedoderawk die einen speziellen Zeichensatz nur übersetzen, und sind daher in Bezug auf die Codegröße ziemlich groß und übersetzen auch keine anderen Sonderzeichen, die codiert werden sollten.

Ein sicherer Weg zum Urlencode wäre, einfach jedes einzelne Byte zu codieren - auch die, die erlaubt gewesen wären.

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

xxd achtet hier darauf, dass die Eingabe als Bytes und nicht als Zeichen behandelt wird.

bearbeiten:

xxd kommt mit dem vim-common-Paket in Debian und ich war gerade auf einem System, auf dem es nicht installiert war und ich wollte es nicht installieren. Der Altornativ ist hexdumpaus dem Paket bsdmainutils in Debian zu verwenden. Gemäß der folgenden Grafik sollten bsdmainutils und vim-common ungefähr gleich wahrscheinlich installiert werden:

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

aber trotzdem hier eine version, die hexdumpanstelle von xxdund verwendet, um den aufruf zu vermeiden tr:

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
Josch
quelle
1
xxd -plainsollte NACH passieren tr -d '\n'!
QDII
3
@qdii warum? Dies würde nicht nur das Urlencode von Zeilenumbrüchen unmöglich machen, sondern auch fälschlicherweise von xxd erstellte Zeilenumbrüche in die Ausgabe einfügen.
Josch
1
@josch. Das ist einfach falsch. Zunächst werden alle \nZeichen von xxd -plainin übersetzt 0a. Nehmen Sie nicht mein Wort dafür, versuchen Sie es selbst: echo -n -e '\n' | xxd -plainDies beweist, dass Ihr tr -d '\n'hier nutzlos ist, da es \nnach xxd -plain Second keine mehr geben kann , echo foobarfügt \nam Ende der Zeichenkette ein eigenes Zeichen hinzu , wird also xxd -plainnicht foobarwie erwartet mit, sondern mit gespeist foobar\n. dann xxd -plain schlägt sich in einem gewissen Zeichenkette es , dass in Enden 0a, ist es nicht geeignet für den Benutzer zu machen. Man könnte hinzufügen , -num echoes zu lösen.
QDII
6
@qdii -n fehlte zwar für Echo, aber der xxdAnruf gehört vor den tr -dAnruf. Es gehört dorthin, so dass jede neue Zeile in von foobarübersetzt wird xxd. Die tr -dnach dem xxdAufruf ist die Zeilenumbrüche zu entfernen , dass xxd produziert. Es scheint, dass Sie nie lange genug xxdFoobar haben, um Zeilenumbrüche zu erzeugen, aber für lange Eingaben wird dies der Fall sein. Das tr -dist also notwendig. Im Gegensatz zu Ihrer Annahme bestand das tr -dNICHT darin, Zeilenumbrüche aus der Eingabe, sondern aus der xxdAusgabe zu entfernen . Ich möchte die Zeilenumbrüche in der Eingabe behalten. Ihr einziger gültiger Punkt ist, dass das Echo eine unnötige neue Zeile hinzufügt.
Josch
1
@qdii und keine Beleidigung genommen - Ich denke nur, dass Sie falsch liegen, außer dem, echo -nwas ich tatsächlich vermisst habe
Josch
62

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

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 von Bruno vorgeschlagen ):

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/'
Sergey
quelle
1
Ich denke, dies ist eine sehr clevere Möglichkeit, die URL-Codierung von cURL wiederzuverwenden.
Solidsnack
13
Das ist absolut genial! Ich wünschte wirklich, Sie hätten eine Zeile hinterlassen, damit die Leute sehen können, wie einfach es wirklich ist. URL-Codierung des Ergebnisses des dateBefehls… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Sie müssen cutdie ersten 2 Zeichen deaktivieren, da die Ausgabe von curl technisch gesehen eine relative URL mit einer
Abfragezeichenfolge ist
2
@BrunoBronosky Ihre Einzeiler-Variante ist gut, fügt aber anscheinend am Ende der Codierung ein "% 0A" hinzu. Benutzer aufgepasst. Die Funktionsversion scheint dieses Problem nicht zu haben.
Levigroker
7
Um dies %0Aam Ende zu vermeiden , verwenden Sie printfanstelle von echo.
Kenorb
2
Der Einzeiler ist fantastisch
Stephen Blum
49

Ich finde es in Python besser lesbar:

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

Das Triple 'stellt sicher, dass einfache Anführungszeichen nicht schaden. urllib ist 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
Sandro
quelle
2
Ich hatte einige Probleme mit Anführungszeichen und Sonderzeichen beim Dreifachzitat. Dies schien im Grunde für alles zu funktionieren: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. write (urllib.quote (sys.stdin.read ())) ")";
Hören Sie auf, Monica Cellio
Python 3-Version wäre 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)'hat fast keine Probleme beim Zitieren und sollte speicher- / geschwindigkeitseffizient sein (nicht überprüft, außer zum Schielen)
Alois Mahdal
2
Es wäre viel sicherer, auf eine Zeichenfolge zu verweisen, sys.argvals sie $valuespäter als Code zu analysieren. Was ist, wenn valueenthalten ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy
2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite
30

Ich habe das folgende Snippet nützlich gefunden, um es in eine Kette 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 )

blau gefärbt
quelle
4
arbeitete für mich. Ich habe es in perl-lpe geändert ... (der Buchstabe ell). Dadurch wurde die nachfolgende Newline entfernt, die ich für meine Zwecke benötigte.
Johnny Lambada
2
Zu Ihrerperl -pe 's/\%(\w\w)/chr hex $1/ge'
Information
2
Abhängig davon, welche Zeichen Sie codieren müssen, können Sie dies vereinfachen, indem Sie perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'Buchstaben, Zahlen und Unterstriche zulassen, aber alles andere codieren.
Robru
23

Wenn Sie GETrequest ausführen und pure curl verwenden möchten, fügen Sie einfach hinzu--get die Lösung von @ Jacob hinzu.

Hier ist ein Beispiel:

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

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 "$@"; 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 ""
    }
' "$@"
MatthieuP
quelle
Gibt es eine einfache Variante, um UTF-8-Codierung anstelle von ASCII zu erhalten?
avgvstvs
15

Dies kann der beste sein:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
Chenzhiwei
quelle
Dies funktioniert bei mir mit zwei Ergänzungen: 1. Ersetzen Sie das -e durch -n, um zu vermeiden, dass am Ende des Arguments eine neue Zeile eingefügt wird, und 2. fügen Sie der printf-Zeichenfolge '%%' hinzu, um ein% vor jedes Paar von zu setzen hexadezimale Ziffern.
Rob Fagen
funktioniert nach $ voraus Klammer hinzufügen after=$(echo -e ...
Roman Rhrn Nesterov
1
Bitte erläutern Sie, wie dies funktioniert. Der odBefehl ist nicht üblich.
Mark Stosberg
Dies funktioniert nicht mit OS X, odda es ein anderes Ausgabeformat als GNU verwendet od. Zum Beispiel printf aa|od -An -tx1 -v|tr \ -druckt -----------61--61--------------------------------------------------------mit OS X odund -61-61mit GNU od. Sie können od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\nentweder mit OS X ododer GNU verwenden od. xxd -p|sed 's/../%&/g'|tr -d \\nmacht das gleiche, obwohl xxdes nicht in POSIX ist, sondern odist.
Nisetama
2
Obwohl dies funktionieren könnte, entgeht es jedem einzelnen Charakter
Charlie
11

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"
}
Davidkammern
quelle
4
Dies verhält sich zwischen den Bash-Versionen unterschiedlich. In RHEL 6.9 ist die Bash 4.1.2 und enthält die einfachen Anführungszeichen. Während Debian 9 und Bash 4.4.12 mit den einfachen Anführungszeichen in Ordnung sind. Für mich hat das Entfernen der einfachen Anführungszeichen bei beiden funktioniert. s = "$ {s // ',' /% 2C}"
muni764
1
Ich habe die Antwort aktualisiert, um Ihre Feststellung widerzuspiegeln, @ muni764.
Davidchambers
Nur eine Warnung ... dies wird Dinge wie den Charakter nicht verschlüsselná
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')

Dadurch wird die Zeichenfolge in $ 1 codiert und in $ url ausgegeben. obwohl Sie es nicht in eine var setzen müssen, wenn Sie wollen. Übrigens hat das sed for tab nicht berücksichtigt, dass es in Leerzeichen umgewandelt werden würde

manoflinux
quelle
5
Ich habe das Gefühl, dass dies nicht der empfohlene Weg ist, dies zu tun.
Cody Gray
2
Erklären Sie bitte Ihr Gefühl ... weil ich, was ich angegeben habe, funktioniert und es in mehreren Skripten verwendet habe, damit ich weiß, dass es für alle Zeichen funktioniert, die ich aufgelistet habe. Erklären Sie daher bitte, warum jemand meinen Code und Perl nicht verwenden würde, da der Titel "URLEncode aus einem Bash-Skript" und kein Perl-Skript lautet.
Manoflinux
Manchmal wird keine
Perlenlösung
3
Dies ist nicht die empfohlene Methode, da eine schwarze Liste eine schlechte Praxis ist und dies ohnehin unfreundlich ist.
Ekevoo
Dies war die freundlichste Lösung, die mit cat file.txt kompatibel ist
mrwaim
7

Für diejenigen unter Ihnen, die nach einer Lösung suchen, die kein Perl 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
   '`
}

Zusammengenäht von ein paar Stellen im Internet und einigen lokalen Versuchen und Irrtümern. Es funktioniert super!

Louis Marascio
quelle
7

uni2ascii ist sehr praktisch:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
kev
quelle
2
Dies funktioniert nicht für Zeichen innerhalb des ASCII-Bereichs, die %-s
Anführungszeichen
7

Sie können Javascript encodeURIComponentin 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 ​​festlegen in .bash_profile:

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

Jetzt können Sie in encodeURIComponent:

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

Wenn Sie sich nicht auf Perl verlassen möchten, können Sie auch sed verwenden. Es ist ein bisschen chaotisch, da jeder Charakter einzeln entkommen muss. Erstellen Sie eine Datei mit dem folgenden Inhalt und rufen Sie sie aufurlencode.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

Gehen Sie wie folgt vor, um es zu verwenden.

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"

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

Sie können dies der Einfachheit halber in ein sh-Skript einfügen, möglicherweise einen Parameter zum Codieren verwenden, es in Ihren Pfad einfügen und dann einfach Folgendes aufrufen:

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

Quelle

Jay
quelle
6

Hier ist die Knotenversion:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\\'}')"
}
Davidkammern
quelle
1
Wird dies nicht unterbrochen, wenn die Zeichenfolge andere Zeichen enthält, die zwischen einfachen Anführungszeichen nicht gültig sind, z. B. ein einzelner Backslash oder Zeilenumbrüche?
Stuart P. Bentley
Guter Punkt. Wenn wir uns die Mühe machen wollen, allen problematischen Charakteren in Bash zu entkommen, können wir die Ersetzungen genauso gut direkt durchführen und nodeganz vermeiden . Ich habe eine Nur-Bash-Lösung veröffentlicht. :)
Davidchambers
1
Diese Variante, die an anderer Stelle auf der Seite zu finden ist, vermeidet das Zitierproblem, indem der Wert von STDIN gelesen wird:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg
6

Die Frage ist, 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. Versuchen Sie es mit dem langen Strich, den Sie von Word erhalten, und Sie erhalten die falsche Codierung.

Beachten Sie, dass "gridsite-clients" installiert sein muss, um diesen Befehl bereitzustellen.

Dylan
quelle
1
Meine Version von Bash (GNU 3.2) hat nicht urlencode. Welche Version verwenden Sie?
Sridhar Sarnobat
1
Ich habe 4.3.42, aber der Befehl urlencode wird von "gridsite-clients" bereitgestellt. Versuchen Sie das zu installieren und es sollte Ihnen gut gehen.
Dylan
5
Ihre Antwort ist also nicht besser als jede, bei der andere Dinge installiert werden müssen (Python, Perl, Lua, ...)
Cyrille Pontvieux
Abgesehen davon, dass nur ein einziges Dienstprogramm anstelle einer ganzen Sprache (und von Bibliotheken) installiert werden muss, ist es sehr einfach und klar zu sehen, was es tut.
Dylan
Ein Link zuerst für die Paket- / Projektseite, die diesen Befehl bereitstellt, wäre hilfreich gewesen.
Doron Behar
6

Einfache PHP-Option:

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

Ruby der Vollständigkeit halber

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

Ein weiterer PHP-Ansatz:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
Jan Halfar
quelle
2
echofügt ein Zeilenumbruchzeichen (hex 0xa) hinzu. Verwenden Sie, um dies zu verhindern echo -n.
Mathew Hall
3

Hier ist meine Version für Busybox Ash Shell für ein eingebettetes 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}"
}
Nulleight
quelle
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

Steven Penny
quelle
2

Hier ist eine einzeilige Konvertierung mit Lua, ähnlich der Antwort von blueyed, außer dass alle nicht reservierten RFC 3986-Zeichen nicht codiert sind (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 Zeilenumbrüche in Ihrer Zeichenfolge von LF in CRLF konvertiert werden. In diesem Fall können Sie eine einfügen gsub("\r?\n", "\r\n") vor der in die Kette .

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

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

Nachdem ich PHP installiert habe, benutze ich diesen Weg:

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

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 codiert? ). 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++
Ray Burgemeestre
quelle
1

Was würde URLs besser analysieren als Javascript?

node -p "encodeURIComponent('$url')"
Nestor Urquiza
quelle
Außerhalb des Anwendungsbereichs. Nicht schlagen, nicht kräuseln. Auch wenn ich sicher bin, dass es sehr gut funktioniert, wenn ein Knoten verfügbar ist.
Cyrille Pontvieux
Warum dies ablehnen und nicht die Python / Perl-Antworten? Außerdem, wie dies nicht die ursprüngliche Frage "Wie Urlencode-Daten für Curl-Befehl?" Beantwortet. Dies kann aus einem Bash-Skript verwendet werden und das Ergebnis kann einem Curl-Befehl übergeben werden.
Nestor Urquiza
Ich habe auch die anderen abgelehnt. Die Frage war, wie man das in einem Bash-Skript macht. Wenn eine andere Sprache wie node / js, python oder perl verwendet wird, muss curl dann nicht direkt verwendet werden.
Cyrille Pontvieux
2
Obwohl ich mich nicht um eine Downvote gekümmert habe, besteht das Problem mit diesem Befehl darin, dass Daten für die Verwendung in Javascript ordnungsgemäß maskiert werden müssen. Versuchen Sie es gerne mit einfachen Anführungszeichen und etwas Backslash-Wahnsinn. Wenn Sie Knoten verwenden möchten, lesen Sie besser Sachen von stdin wienode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - Hacker
1
Seien Sie vorsichtig mit der Lösung von @ MichaelKrelin-hacker, wenn Sie Daten von STDIN einleiten, und stellen Sie sicher, dass Sie keine nachfolgende Newline einfügen. Ist zum Beispiel echo | ...falsch, während echo -n | ...der Zeilenumbruch unterdrückt wird.
Mark Stosberg
0

Das Folgende basiert auf der Antwort von Orwellophile, löst jedoch den in den Kommentaren erwähnten Multibyte-Fehler, indem LC_ALL = C gesetzt wird (ein Trick von vte.sh). Ich habe es in Form einer Funktion geschrieben, die für PROMPT_COMMAND geeignet ist, 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}"
}
Per Bothner
quelle