Как присвоить данные для команды curl?

Я пытаюсь написать bash script для тестирования, который принимает параметр и отправляет его через curl на веб-сайт. Мне нужно, чтобы url закодировал значение, чтобы убедиться, что специальные символы обработаны должным образом. Каков наилучший способ сделать это?

Вот мой основной script:

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

Ответ 1

Использовать curl --data-urlencode; от man curl:

Это данные сообщений, аналогичные другим параметрам --data, за исключением того, что это выполняет кодирование URL. Чтобы быть совместимым с CGI, часть <data> должна начинаться с имени, за которым следует разделитель и спецификация содержимого.

Пример использования:

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

Подробнее см. справочную страницу.

Для этого требуется curl 7.18.0 или новее (выпущено в январе 2008 г.). Используйте curl -V, чтобы проверить, какая у вас версия.

Ответ 2

Вот чистый ответ BASH.

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
}

Вы можете использовать его двумя способами:

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

Ответ 3

Используйте модуль Perl URI::Escape и uri_escape во второй строке вашего bash script:

...

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

Изменить: Исправить проблемы с цитированием, как предлагает Крис Джонсен в комментариях. Спасибо!

Ответ 4

для полноты, многие решения, использующие sed или awk, переводят только специальный набор символов и, следовательно, довольно велики по размеру кода, а также не переводят другие специальные символы, которые должны быть закодированы.

безопасный способ urlencode состоял бы в том, чтобы просто кодировать каждый отдельный байт - даже те, которые были бы разрешены.

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

xxd заботится здесь о том, что вход обрабатывается как байты, а не символы.

изменить:

xxd поставляется с vim-common пакетом в Debian, и я был только в системе, где он не был установлен, и я не хотел его устанавливать. Алтернативным является использование hexdump из пакета bsdmainutils в Debian. Согласно следующему графику, bsdmainutils и vim-common должны иметь равную вероятность для установки:

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

но тем не менее здесь версия, которая использует hexdump вместо xxd и позволяет избежать вызова tr:

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

Ответ 5

Один из вариантов, может быть некрасивым, но простым:

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
}

Вот, например, однострочная версия (предложенная Бруно):

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

Ответ 6

Я нахожу его более читаемым в python:

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

тройка 'гарантирует, что одинарные кавычки в значении не повредят. urllib находится в стандартной библиотеке. Он работает для примера для этого сумасшедшего (реального мира) url:

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

Ответ 7

Другой вариант - использовать jq (в качестве фильтра):

jq -sRr @uri

-R (--raw-input) обрабатывает входные строки как строки, а не анализирует их как JSON, а -sR (--slurp --raw-input) считывает ввод в одну строку. -r (--raw-output) выводит содержимое строк вместо строковых литералов JSON.

Если ввод не является выводом другой команды, вы можете сохранить его в строковой переменной jq:

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

-n (--null-input) не читает ввод, и --arg name value сохраняет value в переменной name в виде строки. В фильтре $name (в одинарных кавычках, чтобы избежать расширения оболочкой) ссылается на переменную name.

Обернутый как функция Bash, это становится:

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

Или этот процент кодирует все байты:

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

Ответ 8

Я нашел следующий фрагмент, полезный для привязки к цепочке вызовов программ, где URI:: Escape не может быть установлен:

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

(источник)

Ответ 9

Если вы хотите запустить запрос GET и использовать чистый curl, просто добавьте --get в решение @Jacob.

Вот пример:

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

Ответ 10

Это может быть лучшим:

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

Ответ 11

Прямая ссылка на версию awk: http://www.shelldorado.com/scripts/cmds/urlencode
Я использовал его в течение многих лет, и он работает как шарм

:
##########################################################################
# 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 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]"

Ответ 12

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')

это будет кодировать строку внутри $1 и выводить ее в $url. хотя вам не нужно класть его в var, если хотите. BTW не включал sed для вкладки, думал, что он превратит его в пробелы.

Ответ 13

Вот решение 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"
}

Ответ 14

Для тех из вас, кто ищет решение, которое не нуждается в perl, здесь нужно только hexdump и awk:

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

Сшитый из нескольких мест по сети и некоторых локальных проб и ошибок. Он отлично работает!

Ответ 15

Если вы не хотите зависеть от 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?%[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"

Это разделит строку на часть, которая нуждается в кодировке, а часть, которая в порядке, закодирует нужную ей часть, затем сшивается вместе.

Вы можете поместить это в sh script для удобства, возможно, потребуется взять параметр для кодирования, поместить его на свой путь, а затем вы можете просто позвонить:

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

источник

Ответ 17

uni2ascii очень удобно:

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

Ответ 18

Вы можете эмулировать javascript encodeURIComponent в perl. Здесь команда:

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

Вы можете установить это как псевдоним bash в .bash_profile:

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

Теперь вы можете подключиться к encodeURIComponent:

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

Ответ 19

Здесь версия node:

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

Ответ 20

Вопрос заключается в том, чтобы сделать это в bash, и нет необходимости в python или perl, поскольку на самом деле есть единственная команда, которая делает именно то, что вы хотите - "urlencode".

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

Это также намного лучше, поскольку, например, приведенный выше perl-ответ не кодирует все символы правильно. Попробуйте с длинной чертой, которую вы получаете от Word, и получите неправильную кодировку.

Примечание. Для предоставления этой команды вам нужны "gridsite-clients".

Ответ 21

Простая опция PHP:

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

Ответ 22

Ruby, для полноты

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

Ответ 23

Другой подход php:

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

Ответ 24

Вот моя версия для загрузочной ящички busybox для встроенной системы, я изначально принял вариант Orwellophile:

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

Ответ 25

Здесь однострочное преобразование с использованием Lua, похожее на blueyed answer, за исключением всего RFC 3986 Unreserved Characters оставил unencoded (например этот ответ):

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

Кроме того, вам может потребоваться убедиться, что строки новой строки в вашей строке преобразуются из LF в CRLF, и в этом случае вы можете вставить gsub("\r?\n", "\r\n") в цепочку до процентного кодирования.

Здесь вариант, который в нестандартном стиле приложения /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 - "$1")

Ответ 26

Вот функция POSIX для этого:

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

Пример:

value=$(encodeURIComponent "$2")

Источник

Ответ 27

После установки php я использую этот способ:

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

Ответ 28

Это версия orwellophile ответа ksh, содержащая функции rawurlencode и rawurldecode (ссылка: Как скопировать данные для команды curl?). Мне не хватает репутации, чтобы опубликовать комментарий, следовательно, новый пост.

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

Ответ 29

Что будет анализировать URL-адреса лучше, чем javascript?

node -p "encodeURIComponent('$url')"

Ответ 30

Следующее основано на ответе Орвеллофила, но решает многобайтовую ошибку, упомянутую в комментариях, устанавливая 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 "\033]7;file://%s%s\007" "${HOSTNAME:-}" "${encoded}"
}