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]
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.
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
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!
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:
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'
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/'
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
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'
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 )
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
Das könnte das Beste sein:
after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
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]"
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
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"
}
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
ni2ascii ist sehr praktisch:
$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
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!
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
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"
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.
Einfache PHP Option:
echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'
Hier ist die Knotenversion:
uriencode() {
node -p "encodeURIComponent('${1//\'/\\\'}')"
}
Ein anderer PHP-Ansatz:
echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
Ruby, der Vollständigkeit halber
value="$(Ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"
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}"
}
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")
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")
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++
Wenn ich PHP installiert habe, benutze ich diesen Weg:
URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"`
Was würde URLs besser analysieren als Javascript?
node -p "encodeURIComponent('$url')"
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}"
}