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


319

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

Вот мой основной сценарий:

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

Смотрите также: Как декодировать строку в кодировке URL в оболочке? для не скручиваемых решений.
— Кенорб

Ответы:


396

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

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

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

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

Смотрите man-страницу для получения дополнительной информации.

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

Вы также можете закодировать строку запроса :

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

5
Кажется, работает только для HTTP POST. Документация здесь: curl.haxx.se/docs/manpage.html#--data-urlencode
— Стэн Джеймс

82
@StanJames Если вы используете его так, curl также может выполнять кодирование для запроса GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
— kberg

13
@kberg на самом деле, это будет работать только для данных запроса. curl добавит '?' с последующим параметром urlencoded. Если вы хотите urlencode некоторого постфикса url (например, CouchDB GET для некоторого идентификатора документа), то --data-urlencode не будет работать.
— Bokeh

1
Не работает curl --data-urlencode "description=![image]($url)" www.example.com. Есть идеи почему? `
— Хуршид Алам

1
@NadavB Ускользающий "‽
— BlackJack

179

Вот чистый ответ 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

4
К сожалению, этот скрипт завершается ошибкой на некоторых символах, таких как 'é' и '½', выводя 'e% FFFFFFFFFFFFFFCC' и '% FFFFFFFFFFFFFFC2' соответственно (я полагаю, b / c цикла для каждого символа).
— Математика

1
Это не работает для меня в Bash 4.3.11 (1). Строка Jogging «à l'Hèze»генерирует Jogging%20%abà%20l%27Hèze%bbчто не может быть decodeURIComponent
— передано

2
В этом первом блоке кода что означает последний параметр для printf? То есть, почему это двойная кавычка, одинарная кавычка, знак доллара, буква с, двойная кавычка? Одинаковая кавычка делает?
— Колин Фрейзер

1
@dmcontador - это простой скромный bash-скрипт, в котором нет концепции многобайтовых символов или юникода. Когда он видит такой символ, как ń ( \u0144), он наивно выдает% 144, ╡ ( \u2561) будет выводить как% 2561. Правильные ответы с кодировкой rawurlended для них будут% C5% 84% 0A и% E2% 95% A1 соответственно.
— Орвеллофил

1
@ColinFraizer одиночная кавычка служит для преобразования следующего символа в его числовое значение. ссылка pubs.opengroup.org/onlinepubs/9699919799/utilities/…
— Сэм,

94

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

...

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

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


2
URI :: Escape может быть не установлен, проверьте мой ответ в этом случае.
— синеватый

Я исправил это (use echo, pipe и <>), и теперь он работает, даже когда $ 2 содержит апостроф или двойные кавычки. Спасибо!
— Дубек

9
Вы также покончили с собой echo:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
— Крис Джонсен

1
Версия Криса Джонсена лучше. У меня было $ {True} в моем тестовом выражении, и использование этого через echo вызвало расширение переменной uri_escape / Perl.
— mm2001

1
@ jrw32982 да, оглядываясь назад, есть другой язык, на котором можно выполнить эту задачу - это хорошо. Если бы я мог, я бы забрал свое пониженное голосование, но, увы, оно сейчас заблокировано.
— thecoshman

69

Другим вариантом является использование 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'

3
<3 это ... должно быть лучшим и приемлемым IMO (да, если вы можете сказать, curlчтобы кодировать это работает, и если bash имеет встроенную функцию, которая была бы приемлемой - но jqкажется подходящей, хотя я далек от достижения уровня комфорта с этот инструмент)
— Nhed

5
для всех, кто интересуется тем же, что и я: @uriне какая-то переменная, а литеральный фильтр jq, используемый для форматирования строк и экранирования; подробности см. в руководстве jq (извините, прямой ссылки нет, нужно искать @uriна странице ...)
— ssc

версия xxd - это именно то, что я искал. Даже если он немного грязный, он короткий и не имеет никаких зависимостей
— Rian Sanderson

1
Пример использования jq для кодирования url:printf "http://localhost:8082/" | jq -sRr '@uri'
— Ашутош Джиндал

67

для полноты картины многие решения, использующие 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'

1
xxd -plainдолжно произойти ПОСЛЕ tr -d '\n'!
— QDII

3
@qdii почему? это не только сделало бы невозможным urlencode новых строк, но также неправильно вставило бы новые строки, созданные xxd, в вывод.
— Йош

1
@josch. Это просто неправильно. Во-первых, любые \nсимволы будут переведены xxd -plainв 0a. Не верьте мне на слово, попробуйте сами: echo -n -e '\n' | xxd -plainэто доказывает, что вы tr -d '\n'здесь бесполезны, так как \nпосле xxd -plain Second их не может быть , echo foobarдобавляется собственный \nсимвол в конец строки символов, поэтому xxd -plainон подается не так, foobarкак ожидалось, а с foobar\n. затем xxd -plain переводит его в некоторую символьную строку, которая заканчивается 0a, что делает его непригодным для пользователя. Вы могли бы добавить -nк echoее решить.
— QDII

6
@qdii действительно -n отсутствует для эха, но xxdвызов принадлежит перед tr -dвызовом. Он принадлежит там так, что любой foobarперевод строки в xxd. tr -dПосле xxdвызова для удаления символов новой строки, XXD производит. Кажется, у вас никогда не было достаточно длинного foobar, чтобы он xxdсоздавал переводы строк, но для длинных входов это будет. Так что tr -dэто необходимо. В отличие от вашего предположения tr -dбыло НЕ убирать переводы строк с ввода, а с xxdвывода. Я хочу сохранить переводы строк во входных данных. Ваш единственный верный аргумент в том, что эхо добавляет ненужный перевод строки.
— Йош

1
@qdii и не обижайся - я просто думаю, что ты не прав, за исключением того, echo -nчего я действительно пропустил
— josch

62

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

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

1
Я думаю, что это очень умный способ повторно использовать кодировку URL cURL.
— solidsnack

13
Это абсолютно великолепно! Мне бы очень хотелось, чтобы вы оставили это в одной строке, чтобы люди могли видеть, насколько все просто на самом деле. Чтобы URL кодировал результат dateкоманды… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Вы должны cutотключить первые 2 символа, потому что выходные данные curl - это технически относительный URL со строкой запроса.)
— Бруно Броноски,

2
@BrunoBronosky Ваш однострочный вариант хорош, но, похоже, добавляет «% 0A» в конец кодировки. Пользователи остерегаются. Версия функции, похоже, не имеет этой проблемы.
— левигрокер

7
Чтобы избежать %0Aв конце, используйте printfвместо echo.
— Кенорб

2
один лайнер фантастический
— Стивен Блюм

49

Я нахожу это более читабельным в 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

2
У меня были некоторые проблемы с кавычками и специальными символами с тройным кавычком, это работало практически для всего: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. write (urllib.quote (sys.stdin.read ())) ")";
— Хватит клеветать на Монику Челлио

Версия Python 3 будет 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)'почти не имеет проблем с цитированием и должен быть эффективным с точки зрения памяти / скорости (не проверял, за исключением косоглазия)
— Alois Mahdal

2
Было бы гораздо безопаснее сослаться на строку, sys.argvа не подставлять ее $valueв строку, которая затем анализируется как код. Что если valueсодержится ''' + __import__("os").system("rm -rf ~") + '''?
— Чарльз Даффи

2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
— Rockallite

30

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

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

( источник )


4
работал на меня. Я изменил его на perl-lpe ... (буква ell). Это убрало завершающий перевод строки, который мне был нужен для моих целей.
— JohnnyLambada

2
К вашему сведению, чтобы сделать обратное, используйте perl -pe 's/\%(\w\w)/chr hex $1/ge'(источник: unix.stackexchange.com/questions/159253/… )
— Шридхар Сарнобат

2
В зависимости от того, какие конкретно символы необходимо кодировать, вы можете упростить это, чтобы perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'разрешить буквы, цифры и подчеркивания, но кодировать все остальное.
— Робру

23

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

Вот пример:

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

15

Прямая ссылка на версию 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 "$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 ""
    }
' "$@"

Есть ли простой вариант получить кодировку UTF-8 вместо ASCII?
— avgvstvs

15

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

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

Это работает для меня с двумя дополнениями: 1. замените -e на -n, чтобы избежать добавления новой строки в конец аргумента, и 2. добавьте «%%» в строку printf, чтобы поместить% перед каждой парой шестнадцатеричные цифры.
— Роб Фаген

работает после добавления $ вперед скобки after=$(echo -e ...
— Роман Ррн Нестеров

1
Пожалуйста, объясните, как это работает. Команда odне является общей.
— Марк Стосберг

Это не работает с OS X, odпотому что он использует другой формат вывода, чем GNU od. Например, printf aa|od -An -tx1 -v|tr \ -печатает -----------61--61--------------------------------------------------------с OS X odи -61-61с GNU od. Вы можете использовать od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\nс OS X odили GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nделает то же самое, хотя xxdне в POSIX, но odесть.
— nisetama

2
Хотя это может сработать, оно ускользает от каждого персонажа
— Чарли

11

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

4
Это ведет себя по-разному между версиями Bash. На RHEL 6.9 bash равен 4.1.2 и включает одинарные кавычки. В то время как Debian 9 и bash 4.4.12 хороши с одинарными кавычками. Для меня удаление одинарных кавычек заставило его работать на обоих. s = "$ {s // ',' /% 2C}"
— muni764

1
Я обновил ответ, чтобы отразить ваши выводы, @ muni764.
— Дэвид Чамберс

Просто предупреждение ... это не будет кодировать такие вещи, как персонажá
— 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')

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


5
У меня такое ощущение, что это не рекомендуемый способ сделать это.
— Коди Грей

2
объясните, пожалуйста, ваши чувства ... потому что я, что я изложил, работает, и я использовал это в нескольких сценариях, поэтому я знаю, что это работает для всех символов, которые я перечислил. поэтому, пожалуйста, объясните, почему кто-то не будет использовать мой код и использовать perl, так как заголовок «URLEncode из сценария bash», а не сценарий perl.
— manoflinux

иногда не нужно жемчужного раствора, так что это может пригодиться
— Yuval Rimar

3
Это не рекомендуемый способ сделать это, потому что черный список - это плохая практика, и в любом случае это unicode.
— Ekevoo

Это было наиболее дружественное решение, совместимое с cat file.txt
— mrwaim


7

Для тех из вас, кто ищет решение, не требующее 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
   '`
}

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


7

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

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

2
Это не работает для символов внутри диапазона ASCII, которые нуждаются в кавычках, как %и пробел (последнее можно исправить с помощью -sфлага)
— Boldewyn

7

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

источник


7

Вы можете эмулировать 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!

6

Вот версия узла:

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

1
Не нарушится ли это, если в строке есть другие символы, которые недопустимы между одинарными кавычками, например, обратная косая черта или перевод строки?
— Стюарт П. Бентли

Хорошая точка зрения. Если мы хотим избежать побега всех проблемных персонажей в Bash, мы могли бы также выполнить замены напрямую и nodeвообще избежать . Я разместил решение только для Bash. :)
— Давидчамберс

1
Этот вариант, найденный в другом месте на странице, позволяет избежать проблемы цитирования, считывая значение из STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
— Марк Стосберг

6

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

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

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

Обратите внимание, что для обеспечения этой команды вам необходимо установить «gridsite-clients».


1
Моя версия bash (GNU 3.2) не имеет urlencode. Какую версию ты используешь?
— Шридхар Сарнобат

1
У меня 4.3.42, но команда urlencode предоставляется "gridsite-clients". Попробуйте установить это, и все будет в порядке.
— Дилан

5
Таким образом, ваш ответ не лучше, чем любой, требующий установки других вещей (python, perl, lua,…)
— Cyrille Pontvieux

За исключением того, что требуется только установка одной утилиты вместо всего языка (и библиотек), плюс очень просто и понятно, что она делает.
— Дилан

Ссылка сначала на страницу пакета / проекта с этой командой была бы полезна.
— Дорон Бехар


4

Рубин, для полноты

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

4

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

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

2
echoдобавит символ новой строки (hex 0xa). Чтобы остановить это, используйте echo -n.
— Мэтью Холл

3

Вот моя версия для busybox ash shell для встроенной системы, я изначально принял вариант 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}"
}

2

Вот функция 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")

Источник


2

Вот преобразование в одну строку с использованием Lua, похожее на ответ blueyed, за исключением того, что все незарезервированные символы RFC 3986 остались незакодированными (как этот ответ ):

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

Кроме того, вам может потребоваться убедиться, что переводы строки в вашей строке преобразуются из 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 - "$1")

1

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

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

1

Это ksh-версия ответа orwellophile, содержащая функции rawurlencode и rawurldecode (ссылка: Как urlencode данных для команды 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++

1

Что будет разбирать URL лучше, чем JavaScript?

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

Вне сферы действия вопроса. Не баш, не локон. Даже если я уверен, что работает очень хорошо, если узел доступен.
— Сирил Понтьё

Почему это голосование с понижением, а не с ответами на python / perl? Кроме того, как это не отвечает на оригинальный вопрос «Как urlencode данных для команды curl?». Это может быть использовано из скрипта bash, а результат может быть передан команде curl.
— Нестор

Я тоже проголосовал против остальных. Вопрос был в том, как сделать это в скрипте bash. Если используется другой язык, такой как node / js, python или perl, тогда нет необходимости использовать curl напрямую.
— Сирил Понтвье

2
В то время как я не удосужился понизить голос, проблема с этой командой состоит в том, что она требует, чтобы данные были должным образом экранированы для использования в javascript. Как попробовать это с одинарными кавычками и некоторым безумием с обратной косой чертой. Если вы хотите использовать нод, вам лучше читать материал из stdin, напримерnode -p 'encodeURIComponent(require("fs").readFileSync(0))'
— Michael Krelin - хакер

1
Будьте осторожны с решением @ MichaelKrelin-hacker, если вы передаете данные из STDIN, убедитесь, что не добавляете завершающий символ новой строки. Например, echo | ...неправильно, хотя echo -n | ...подавляет перевод строки.
— Марк Стосберг

0

Следующее основано на ответе Орвеллофила, но решает многобайтовую ошибку, упомянутую в комментариях, путем установки 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}"
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.