Як urlencode дані для команди curl?


319

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

Ось мій базовий сценарій поки що:

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

Дивіться також: Як розшифрувати рядок, закодовану URL-адресою в оболонці? для розгорнутих розчинів.
kenorb

Відповіді:


395

Використання curl --data-urlencode; від man curl:

Він публікує дані, подібні до інших --dataпараметрів, за винятком того, що це виконує кодування URL-адрес. Щоб відповідати CGI, <data>частина повинна починатись з імені, за яким слід роздільник та специфікація вмісту.

Приклад використання:

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

Додаткову інформацію див. На сторінці чоловіка .

Для цього потрібна згортання 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 додасть "?" Слідом за кодами, що кодуються урленом. Якщо ви хочете urlencode деякий поштовий індекс (наприклад, CouchDB GET для деякого ідентифікатора документа), то "--data-urlencode" не працюватиме.
Боке

1
Не працює для curl --data-urlencode "description=![image]($url)" www.example.com. Будь-яка ідея чому? `
Хуршид Алам

1
@NadavB Escaping "the‽
BlackJack

179

Ось чистий відповідь БАШ.

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% FFFFFFFFFFFFFFFFCC" і "% FFFFFFFFFFFFFFC2" відповідно (b / c циклу для кожного символу, я вважаю).
Matthemattics

1
Він не працює для мене в Bash 4.3.11 (1). Рядок Jogging «à l'Hèze»генерує, Jogging%20%abà%20l%27Hèze%bbщо не може бути decodeURIComponent
поданий

2
Що в цьому першому блоці коду означає останній параметр printf? Тобто, чому це подвійна цитата, одноцитата, знак долара, літера-с, подвійна цитата? Чи робить одноцінку?
Колін Фрейзер

1
@dmcontador - це лише скромний скрипт bash, він не має концепції багатобайтових символів або unicode. Коли він побачить такий символ, як ń ( \u0144), він наївно виведе% 144, ╡ ( \u2561) виведеться як% 2561. Правильні відповіді, кодовані сировиною , будуть% 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 може бути встановлений, перевірте мою відповідь у такому випадку.
синюха

Я виправив це (використання echo, передача та <>), і тепер він працює навіть тоді, коли в 2 доларах міститься апостроф або подвійні лапки. Дякую!
дубек

9
Ви echoтеж value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
ухиляєтесь

1
Версія Кріса Джонсена краща. Я мав $ {True} у своєму тестовому виразі і використовуючи це за допомогою відлуння, запускається розширення змінної 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 це ... повинно бути прийнятим ІМО (так, якщо ви можете сказати, curlщо кодувати, що працює, і якщо в баші є вбудований, що було б прийнятним, - але 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 другого не може бути жодного , 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 виробляє. Здається, ви ніколи не маєте достатньо довгого часу, щоб 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.
солідарний матч

13
Це абсолютно геніально! Я дуже хочу, щоб ти залишив це єдиною лінією, щоб люди могли бачити, наскільки це просто. Для кодування URL-адреси результату dateкоманди… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Ви маєте cutперші 2 символи відключити, оскільки вихід curl є технічно відносною URL-адресою з рядком запиту.)
Бруно Броноскі,

2
@BrunoBronosky Ваша однолінійна версія хороша, але, здавалося б, додає "% 0A" до кінця кодування. Користувачі будьте обережні. Здається, у версії функції ця проблема не виникає.
levigroker

7
Щоб уникнути %0Aв кінці, використовуйте printfзамість echo.
kenorb

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. написати (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)'майже не має проблем із котируванням, і має бути ефективною пам'яттю / швидкістю (не перевіряли, окрім
косинців

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
FYI, щоб зробити це зворотним, скористайтеся perl -pe 's/\%(\w\w)/chr hex $1/ge'(джерело: unix.stackexchange.com/questions/159253/… )
Шрідхар Сарнобат

2
Залежно від конкретних символів, які потрібно кодувати, ви можете спростити це, perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'яке дозволяє літери, цифри та підкреслення, але кодує все інше.
robru

23

Якщо ви хочете запустити GETзапит і використовувати чисту завивку, просто додайте --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--------------------------------------------------------з ОС X odі -61-61GNU 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є.
нісетама

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. хоча вам не потрібно ставити його у вар, якщо хочете. До речі, BTW не включав sed для вкладки, він перетворив його на пробіли


5
Я відчуваю, що це не рекомендований спосіб зробити це.
Коді Грей

2
поясніть своє почуття, будь ласка .... тому що я те, що я заявив, працює, і я використав це в декількох сценаріях, так що я знаю, що це працює для всіх перелічених нами знаків. тому, будь ласка, поясніть, чому хтось не використовує мій код і не використовує perl, оскільки назва цього "URLEncode від bash script", а не сценарій perl.
manoflinux

іноді не потрібен перловий розчин, щоб це могло стати в нагоді
Юваль Римар

3
Це не рекомендований спосіб зробити це, оскільки чорний список - це погана практика, і це все одно недружелюбно.
Екевоо

Це було найбільш дружнє рішення, сумісне з 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, і ви отримаєте неправильне кодування.

Зауважте, для надання цієї команди вам потрібні "клієнти сітки".


1
Моя версія bash (GNU 3.2) не має urlencode. Яку версію ви використовуєте?
Шрідхар Сарнобат

1
У мене 4.3.42, але команду urlencode надають "gridsite-clients". Спробуйте встановити це, і вам слід добре.
Ділан

5
Тож ваша відповідь не краща за будь-яку, що вимагає встановлення інших речей (python, perl, lua,…)
Cyrille Pontvieux

За винятком того, що для цього потрібно лише встановити єдину утиліту замість цілої мови (і бібліотек), плюс це дуже просто і зрозуміло, щоб побачити, що вона робить.
Ділан

Перше посилання на сторінку пакета / проекту, що забезпечує цю команду, було б корисним.
Дорон Бехар

6

Простий варіант PHP:

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

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додасть символ нового рядка (шістнадцятковий 0xa). Щоб перестати це робити, використовуйте echo -n.
Меттью Холл

3

Ось моя версія для оболонки золи для вбудованої системи для вбудованої системи, я спочатку прийняв варіант 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") у ланцюг перед кодуванням відсотків.

Ось варіант, який у нестандартному стилі застосування / 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

Це 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.
Нестор Уркіза

Я проголосував і за інших. Питання полягало в тому, як це зробити в баш-скрипті. Якщо використовується інша мова, як node / js, python або perl, тоді немає необхідності безпосередньо використовувати curl.
Кирилла Понтьє

2
У той час як я не намагався підкреслити, проблема з цією командою полягає в тому, що вона вимагає належних даних для використання в JavaScript. Як би спробувати це з одинарними цитатами та деяким божевільним зворотом. Якщо ви хочете скористатись вузлом, то краще прочитайте речі із stdin, наприкладnode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - хакер

1
Будьте обережні з рішенням @ MichaelKrelin-хакера, якщо ви збираєте дані в 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.