Как urlencode данные для команды curl?
Я пытаюсь написать сценарий bash для тестирования, который принимает параметр и отправляет его через curl на веб-сайт. Мне нужно url-адрес кодировать значение, чтобы убедиться, что специальные символы обрабатываются правильно. Как лучше всего это сделать?
вот мой основной скрипт до сих пор:
#!/bin/bash
host=${1:?'bad host'}
value=
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@
30 ответов
использовать curl --data-urlencode
; С man curl
:
это публикует данные, похожие на другие
--data
options за исключением того, что это выполняет URL-кодирование. Чтобы быть CGI-совместимым,<data>
часть должна начинаться с имени, за которым следует разделитель и спецификация содержание.
пример использования:
curl \
--data-urlencode "paramName=value" \
--data-urlencode "secondParam=value" \
http://example.com
посмотреть man page для получения дополнительной информации.
для этого требуется curl 7.18.0 или новее (выпущен в январе 2008). Использовать curl -V
чтобы проверить, какая версия у вас.
вот чистый ответ BASH.
rawurlencode() {
local string=""
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
}
вы можете использовать его двумя способами:
easier: echo http://url/q?=$( rawurlencode "$args" )
faster: rawurlencode "$args"; echo http://url/q?${REPLY}
[отредактировано]
вот соответствующая функция rawurldecode (), которая - со всей скромностью - потрясающая.
# 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
}
С соответствующим набором, теперь мы можем выполнить некоторые простые тесты:
$ diff rawurlencode.inc.sh \
<( rawurldecode "$( rawurlencode "$( cat rawurlencode.inc.sh )" )" ) \
&& echo Matched
Output: Matched
и если вы действительно чувствуете, что вам нужен внешний инструмент (ну, он будет идти намного быстрее, и может делать двоичных файлов и тому подобное...) Я нашел это на моем Маршрутизатор OpenWRT...
replace_value=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed)
где url_escape.sed был файлом, в котором содержались следующие правила:
# 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
используйте Perl URI::Escape
модуль uri_escape
функция во второй строке вашего скрипта bash:
...
value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "")"
...
Edit: исправить проблемы с цитированием, как предложил Крис Джонсен в комментариях. Спасибо!
для полноты, многие решения с использованием sed
или awk
только переводить специальный набор символов и, следовательно, довольно большие по размеру кода, а также не переводить другие специальные символы, которые должны быть закодированы.
безопасным способом urlencode было бы просто кодировать каждый байт - даже те, которые были бы разрешены.
echo -ne 'some random\nbytes' | xxd -plain | tr -d '\n' | sed 's/\(..\)/%/g'
xxd заботится о том, чтобы вход обрабатывался как байты, а не письмена.
edit:
xxd поставляется с пакетом vim-common в Debian, и я был только в системе, где он не был установлен, и я не хотел его устанавливать. В altornative заключается в использовании hexdump
из пакета bsdmainutils в Debian. Согласно следующему графику, bsdmainutils и vim-common должны иметь примерно равную вероятность быть установлено:
но все же здесь версия, которая использует hexdump
вместо xxd
и позволяет избежать tr
звоните:
echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%/g'
Я нахожу его более читаемым в python:
encoded_value=$(python -c "import urllib; print urllib.quote('''$value''')")
тройка " гарантирует, что одинарные кавычки в значении не повредят. urllib находится в стандартной библиотеке. Он работает для exampple для этого сумасшедшего (реального мира) url:
"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
один из вариантов, может быть коряво, но просто:
urlencode() {
local data
if [[ $# != 1 ]]; then
echo "Usage: string-to-urlencode"
return 1
fi
data="$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "" "")"
if [[ $? != 3 ]]; then
echo "Unexpected error" 1>&2
return 2
fi
echo "${data##/?}"
return 0
}
вот однострочная версия, например (как предложено Бруно):
date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-
Я нашел следующий фрагмент, полезный для вставки его в цепочку вызовов программы, где URI:: Escape может не быть установлен:
perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord())/seg'
(источник)
Если вы хотите использовать GET
запрос и использование pure curl просто добавьте --get
к решению @Jacob.
вот пример:
curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
Другой вариант-использовать jq
:
jq -s -R -r @uri
-s
(--slurp
) считывает входные строки в массив и -s -R
(--slurp --raw-input
) считывает входные данные в одну строку. -r
(--raw-output
) выводит содержимое строк вместо строковых литералов JSON.
или этот процент-кодирует все байты:
xxd -p|tr -d \n|sed 's/../%&/g'
прямая ссылка на версию awk:http://www.shelldorado.com/scripts/cmds/urlencode
Я использовал его в течение многих лет и он работает как шарм
:
##########################################################################
# 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 ""` # 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 "" 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 (); ++i ) {
c = substr (, 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 ""
}
' "$@"
url=$(echo "" | 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')
это будет кодировать строку внутри $1 и выводить ее в $url. хотя, если хочешь, можешь не вставлять его в ВАР. Кстати, не включал sed для вкладки, думал, что он превратит его в пробелы
для тех из вас, кто ищет решение, которое не нуждается в perl, вот тот, который нуждается только в hexdump и awk:
url_encode() {
[ $# -lt 1 ] && { return; }
encodedurl="";
# 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 '
== "20" { printf("%s", "+"); next } # space becomes plus
~ /0[adAD]/ { next } # strip newlines
~ /^[a-zA-Z0-9.*()\/-]$/ { printf("%s", ); next } # pass through what we can
{ printf("%%%s", ) } # take hex value of everything else
'`
}
сшитые вместе из нескольких мест через сеть и некоторые локальные проб и ошибок. Он отлично работает!
использование php из сценария оболочки:
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"
uni2ascii - Это очень кстати:
$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
если вы не хотите зависеть от Perl, вы также можете использовать sed. Это немного грязно, так как каждый персонаж должен быть экранирован индивидуально. Создайте файл со следующим содержимым и назовите его 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
чтобы использовать его, выполните следующие действия.
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"
это разделит строку на часть, которая нуждается в кодировке, и часть, которая в порядке, кодирует часть, которая нуждается в ней, а затем сшивает вместе.
вы можете поместить это в сценарий sh для удобства, возможно, у него есть параметр для кодирования, поместите его на свой путь, а затем вы можете просто позвонить:
urlencode https://www.exxample.com?isThisFun=HellNo
вопрос в том, чтобы сделать это в bash, и нет необходимости в python или perl, поскольку на самом деле есть одна команда, которая делает именно то, что вы хотите - "urlencode".
value=$(urlencode "")
Это также намного лучше, так как приведенный выше ответ perl, например, не кодирует все символы правильно. Попробуйте с длинной черточкой, которую вы получаете от Word, и вы получите неправильную кодировку.
обратите внимание, вам нужен "gridsite-клиентов" установлен, чтобы обеспечить эту команду.
вы можете эмулировать javascript encodeURIComponent
в perl. Вот команда:
perl -pe 's/([^a-zA-Z0-9_.!~*()'\''-])/sprintf("%%%02X", ord())/ge'
вы можете установить это как псевдоним bash в .bash_profile
:
alias encodeURIComponent='perl -pe '\''s/([^a-zA-Z0-9_.!~*()'\''\'\'''\''-])/sprintf("%%%02X",ord())/ge'\'
теперь вы можете трубы в encodeURIComponent
:
$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!
вот решение Bash, которое не вызывает никаких внешних программ:
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"
}
вот функция POSIX для этого:
encodeURIComponent() {
awk 'BEGIN {while (y++ < 125) z[sprintf("%c", y)] = y
while (y = substr(ARGV[1], ++j, 1))
q = y ~ /[[:alnum:]_.!~*()-]/ ? q y : q sprintf("%%%02X", z[y])
print q}' ""
}
пример:
value=$(encodeURIComponent "")
вот однострочное преобразование с использованием Lua, аналогичное blueyed это!--5--> только с RFC 3986 неограниченные символы оставлено без кода (например ответ):
url=$(echo 'print((arg[1]:gsub("([^%w%-%.%_%~])",function(c)return("%%%02X"):format(c:byte())end)))' | lua - "")
кроме того, вам может потребоваться убедиться, что новые строки в вашей строке преобразуются из LF в CRLF, и в этом случае вы можете вставить gsub("\r?\n", "\r\n")
в цепочке перед процентным кодированием.
вот вариант, который, в нестандартный стиль application / x-www-form-urlencoded, выполняет ли эта нормализация новой строки, а также кодирование пробелов как " + "вместо" %20 " (что, вероятно, может быть добавлено в фрагмент Perl с использованием аналогичной техники).
url=$(echo 'print((arg[1]:gsub("\r?\n", "\r\n"):gsub("([^%w%-%.%_%~ ]))",function(c)return("%%%02X"):format(c:byte())end):gsub(" ","+"))' | lua - "")
Это версия KSH ответа orwellophile, содержащая функции rawurlencode и rawurldecode (ссылка:Как urlencode данные для команды curl?). У меня недостаточно репутации, чтобы опубликовать комментарий, следовательно, новый пост..
#!/bin/ksh93
function rawurlencode
{
typeset string=""
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++
вот моя версия для оболочки busybox ash для встроенной системы, я изначально принял вариант Orwellophile:
urlencode()
{
local S=""
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}"
}
следующее основано на ответе Orwellophile, но решает многобайтовый ошибка, упомянутая в комментариях, установив LC_ALL=C (трюк из vte.sh). Я написал его в виде функции, подходящей PROMPT_COMMAND, потому что вот как я это использую.
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 "3]7;file://%s%s7" "${HOSTNAME:-}" "${encoded}"
}