Jak urlencode dane dla polecenia curl?

319

Próbuję napisać skrypt bash do testowania, który pobiera parametr i wysyła go przez curl do strony internetowej. Muszę zakodować adres URL, aby upewnić się, że znaki specjalne są poprawnie przetwarzane. Jak najlepiej to zrobić?

Oto mój podstawowy skrypt do tej pory:

#!/bin/bash
host=${1:?'bad host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@
Aaron
źródło
Zobacz także: Jak dekodować ciąg zakodowany w adresie URL w powłoce? dla rozwiązań nie zwijających się.
Kenorb

Odpowiedzi:

395

Użyj curl --data-urlencode; z man curl:

Publikuje dane, podobnie jak inne --dataopcje, z tym wyjątkiem, że wykonuje kodowanie adresów URL. Aby być zgodnym z CGI, <data>część powinna zaczynać się od nazwy, po której następuje separator i specyfikacja treści.

Przykładowe użycie:

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

Zobacz stronę manuala aby uzyskać więcej informacji.

Wymaga to curl 7.18.0 lub nowszego (wydanego w styczniu 2008) . Posługiwać się curl -V aby sprawdzić, którą wersję posiadasz.

Możesz również zakodować ciąg zapytania :

curl -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202
Jacob Rask
źródło
5
Wydaje się, że działa tylko dla HTTP POST. Dokumentacja tutaj: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James
82
@StanJames Jeśli używasz go tak, aby curl mógł również kodować żądanie GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
kberg
13
@kberg faktycznie będzie to działać tylko w przypadku danych zapytań. curl doda „?” a następnie parametry zapisane w urlencoded. Jeśli chcesz urlencode trochę postfix url (np. CouchDB GET dla jakiegoś dokumentu id), wtedy '--data-urlencode' nie będzie działać.
Bokeh
1
Nie działa dla curl --data-urlencode "description=![image]($url)" www.example.com. Masz pomysł, dlaczego? `
Khurshid Alam
1
@NadavB Escaping "the‽
BlackJack
179

Oto czysta odpowiedź 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
}

Możesz użyć go na dwa sposoby:

easier:  echo http://url/q?=$( rawurlencode "$args" )
faster:  rawurlencode "$args"; echo http://url/q?${REPLY}

[edytowany]

Oto pasująca funkcja rawurldecode (), która - przy całej skromności - jest niesamowita.

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

Dzięki pasującemu zestawowi możemy teraz wykonać kilka prostych testów:

$ diff rawurlencode.inc.sh \
        <( rawurldecode "$( rawurlencode "$( cat rawurlencode.inc.sh )" )" ) \
        && echo Matched

Output: Matched

A jeśli naprawdę czujesz, że potrzebujesz zewnętrznego narzędzia (cóż, będzie działać znacznie szybciej i może tworzyć pliki binarne itp.) Znalazłem to na moim routerze OpenWRT ...

replace_value=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed)

Gdzie url_escape.sed był plikiem zawierającym następujące reguły:

# 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
Orwellophile
źródło
4
Niestety, ten skrypt nie działa na niektórych znakach, takich jak „é” i „½”, generując odpowiednio „e% FFFFFFFFFFFFFFCC” i „% FFFFFFFFFFFFFFC2” (wydaje mi się, że jest to część b / c pętli na znak).
Matthemattics
1
Nie działa dla mnie w Bash 4.3.11 (1). Jogging «à l'Hèze»Generuje ciąg, Jogging%20%abà%20l%27Hèze%bbktóry nie może być przesłany do JS decodeURIComponent:(
dmcontador,
2
W tym pierwszym bloku kodu, co oznacza ostatni parametr printf? To znaczy, dlaczego jest to podwójny cytat, pojedynczy cytat, znak dolara, litera c, podwójny cytat? Czy robi to pojedynczy cytat?
Colin Fraizer
1
@dmcontador - to tylko skromny skrypt bashowy, nie ma koncepcji znaków wielobajtowych ani Unicode. Kiedy zobaczy znak taki jak ń ( \u0144), naiwnie wyświetli% 144, ╡ ( \u2561) zostanie wyprowadzony jako% 2561. Prawidłowe odpowiedzi dla tych kodów będą odpowiednio% C5% 84% 0A i% E2% 95% A1.
Orwellophile
1
@ColinFraizer pojedynczy cytat służy do przekształcenia następującego znaku w jego wartość liczbową. ref. pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Sam
94

Użyj URI::Escapemodułu i uri_escapefunkcji Perla w drugiej linii skryptu bash:

...

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

Edycja: Napraw problemy z cytowaniem, jak sugerował Chris Johnsen w komentarzach. Dzięki!

dubek
źródło
2
URI :: Escape może nie być zainstalowany, w takim przypadku sprawdź moją odpowiedź.
niebieskawy
Naprawiłem to (użyj echo, potok i <>), a teraz działa nawet wtedy, gdy 2 $ zawiera apostrof lub podwójne cudzysłowy. Dzięki!
dubek
9
Pozbywasz się echorównież:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen
1
Wersja Chrisa Johnsena jest lepsza. Miałem $ {True} w moim wyrażeniu testowym i użycie tego przez echo spowodowało rozwinięcie zmiennej uri_escape / Perl.
mm2001
1
@ jrw32982 tak, patrząc wstecz, dobry język, w którym można wykonać to zadanie, jest dobry. Gdybym mógł, wycofałbym moją opinię, ale niestety jest ona obecnie zamknięta.
thecoshman
69

Inną opcją jest użycie jq(jako filtru):

jq -sRr @uri

-R( --raw-input) traktuje wiersze wejściowe jako ciągi zamiast analizować je jako JSON, a -sR( --slurp --raw-input) odczytuje dane wejściowe w jednym ciągu. -r(--raw-output ) wyświetla zawartość ciągów zamiast literałów ciągów JSON.

Jeśli dane wejściowe nie są danymi wyjściowymi innego polecenia, możesz je zapisać w jqzmiennej łańcuchowej:

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

-n( --null-input) nie odczytuje danych wejściowych i --arg name valuezapisuje valuew zmiennej namejako ciąg znaków. W filtrze $name(w pojedynczych cudzysłowach, aby uniknąć rozwinięcia przez powłokę), odwołuje się do zmiennej name.

Opakowany jako funkcja Bash, staje się:

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

Lub ten procent koduje wszystkie bajty:

xxd -p|tr -d \\n|sed 's/../%&/g'
nisetama
źródło
3
<3 to ... powinno być najlepsze i zaakceptowane IMO (tak, jeśli możesz powiedzieć, curlżeby zakodować to działa, a jeśli bash ma wbudowane, które byłoby akceptowalne - ale jqwydaje się, że jest to właściwe dopasowanie, ale jestem daleki od osiągnięcia poziomu komfortu z to narzędzie)
nhed
5
dla każdego, kto zastanawia się nad tym samym, co ja: @urinie jest jakąś zmienną, ale dosłowny filtr jq używany do formatowania ciągów znaków i zmiany znaczenia; szczegóły w instrukcji jq (przepraszam, brak bezpośredniego linku, trzeba szukać @urina stronie ...)
ssc
wersja xxd była właśnie tym, czego szukałem. Nawet jeśli jest trochę brudny, jest krótki i nie ma zależności
Rian Sanderson
1
Przykładowe użycie jq do kodowania adresu URL:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal
67

ze względu na kompletność, wiele rozwiązań wykorzystujących sedlub awktłumaczących specjalny zestaw znaków, a zatem jest dość dużych pod względem rozmiaru kodu, a także nie tłumaczy innych znaków specjalnych, które powinny być zakodowane.

bezpiecznym sposobem na urlencode byłoby po prostu zakodowanie każdego bajtu - nawet tych, które byłyby dozwolone.

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

xxd dba o to, aby dane wejściowe były traktowane jako bajty, a nie znaki.

edytować:

xxd jest dostarczany z pakietem vim-common w Debianie, a ja byłem na systemie, w którym nie został zainstalowany i nie chciałem go instalować. Altornatywą jest użyciehexdump z pakietu bsdmainutils w Debianie. Zgodnie z poniższym wykresem prawdopodobieństwo zainstalowania bsdmainutils i vim-common powinno być prawie równe:

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

ale tutaj jest wersja, która używa hexdumpzamiast xxdi pozwala uniknąć trpołączenia:

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
josch
źródło
1
xxd -plainpowinno zdarzyć się PO tr -d '\n'!
qdii
3
@qdii dlaczego? to nie tylko uniemożliwiłoby urlencode nowego wiersza, ale również błędnie wstawiłoby nowe wiersze utworzone przez xxd do wyniku.
josch
1
@josch. To jest po prostu źle. Po pierwsze, wszystkie \nznaki zostaną przetłumaczone xxd -plainna 0a. Nie wierz mi na słowo, spróbuj sam: echo -n -e '\n' | xxd -plainto dowodzi, że twój tr -d '\n'jest tutaj bezużyteczny, ponieważ nie może być żadnego \npo xxd -plain Second, echo foobardodaje swój własny \nznak na końcu łańcucha znaków, więc xxd -plainnie jest karmiony foobarzgodnie z oczekiwaniami, ale z foobar\n. następnie xxd -plain tłumaczy go na ciąg znaków, który kończy się na 0a, co czyni go nieodpowiednim dla użytkownika. Można dodać -ndo echojego rozwiązania.
qdii
6
@qdii rzeczywiście -n brakowało echa, ale xxdpołączenie należy przed tr -dpołączeniem. Należy tam, aby każda nowa linia foobarzostała przetłumaczona przez xxd. tr -dPo xxdwywołaniu jest usunięcie newlines że xxd produkuje. Wygląda na to, że nigdy nie masz foobara wystarczająco długiego, aby xxdprodukować nowe linie, ale przy długich wejściach tak będzie. Więc to tr -djest konieczne. W przeciwieństwie do twojego założenia tr -dNIE było usuwania nowych linii z danych wejściowych, ale z danych xxdwyjściowych. Chcę zachować nowe wiersze na wejściu. Twoim jedynym słusznym punktem jest to, że echo dodaje niepotrzebną nową linię.
josch
1
@qdii i bez przestępstwa - myślę, że się mylisz, z wyjątkiem tego, echo -nczego tak naprawdę brakowało
josch
62

Jeden z wariantów może być brzydki, ale prosty:

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
}

Oto na przykład wersja jednoliniowa (zgodnie z sugestią Bruno ):

date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-

# If you experience the trailing %0A, use
date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | sed -E 's/..(.*).../\1/'
Siergiej
źródło
1
Myślę, że to bardzo sprytny sposób na ponowne wykorzystanie kodowania URL cURL.
solidsnack
13
To jest absolutnie genialne! Naprawdę chciałbym, abyście zostawili jedną linię, aby ludzie mogli zobaczyć, jakie to naprawdę proste. Aby adres URL zakodować wynik datepolecenia… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Musisz cutwyłączyć pierwsze 2 znaki, ponieważ wyjście curl jest technicznie względnym adresem URL z ciągiem zapytania.)
Bruno Bronosky
2
@BrunoBronosky Twój wariant z jedną linią jest dobry, ale pozornie dodaje „% 0A” na końcu kodowania. Użytkownicy strzeżcie się. Wydaje się, że wersja funkcji nie ma tego problemu.
levigroker,
7
Aby uniknąć %0Ana końcu, użyj printfzamiast echo.
kenorb
2
jeden liniowiec jest fantastyczny
Stephen Blum
49

Uważam, że jest bardziej czytelny w Pythonie:

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

potrójny „gwarantuje, że pojedyncze cytaty wartości nie zaszkodzą. urllib znajduje się w standardowej bibliotece. To działa na przykład dla tego zwariowanego (prawdziwego świata) adresu URL:

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
sandro
źródło
2
Miałem pewne problemy z cudzysłowami i specjalnymi znakami przy potrójnym cytowaniu, wydawało się, że działa to w zasadzie na wszystko: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import sys; sys.stdout. write (urllib.quote (sys.stdin.read ())) ")";
Przestań oczerniać Monikę Cellio
Byłaby wersja 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)'prawie nie ma problemów z cytowaniem i powinien być efektywny pod względem pamięci / prędkości (nie sprawdzono, z wyjątkiem
zezowania
2
O wiele bezpieczniej byłoby odwoływać się do niego sys.argvzamiast zastępować $valuego łańcuchem analizowanym później jako kod. Co jeśli valuezawarte ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy
2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite
30

Uważam, że następujący fragment kodu jest przydatny do umieszczenia go w łańcuchu wywołań programu, w których URI :: Escape może nie być zainstalowany:

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

( źródło )

niebieskawy
źródło
4
pracował dla mnie. Zmieniłem go na perl-lpe ... (litera ell). To usunęło końcowy znak nowej linii, którego potrzebowałem do swoich celów.
JohnnyLambada
2
FYI, aby zrobić odwrotność tego, użycie perl -pe 's/\%(\w\w)/chr hex $1/ge'(źródło: unix.stackexchange.com/questions/159253/... )
Sridhar Sarnobat
2
W zależności od konkretnych znaków, które musisz zakodować, możesz uprościć to, perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'co pozwala na litery, cyfry i podkreślenia, ale koduje wszystko inne.
robru
23

Jeśli chcesz uruchomić GETżądanie i użyć czystego curl, po prostu dodaj --getdo rozwiązania @ Jacob.

Oto przykład:

curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
Piotr Czapla
źródło
15

Bezpośredni link do wersji awk: http://www.shelldorado.com/scripts/cmds/urlencode
Używałem go od lat i działa jak urok

:
##########################################################################
# Title      :  urlencode - encode URL data
# Author     :  Heiner Steven ([email protected])
# Date       :  2000-03-15
# Requires   :  awk
# Categories :  File Conversion, WWW, CGI
# SCCS-Id.   :  @(#) urlencode  1.4 06/10/29
##########################################################################
# Description
#   Encode data according to
#       RFC 1738: "Uniform Resource Locators (URL)" and
#       RFC 1866: "Hypertext Markup Language - 2.0" (HTML)
#
#   This encoding is used i.e. for the MIME type
#   "application/x-www-form-urlencoded"
#
# Notes
#    o  The default behaviour is not to encode the line endings. This
#   may not be what was intended, because the result will be
#   multiple lines of output (which cannot be used in an URL or a
#   HTTP "POST" request). If the desired output should be one
#   line, use the "-l" option.
#
#    o  The "-l" option assumes, that the end-of-line is denoted by
#   the character LF (ASCII 10). This is not true for Windows or
#   Mac systems, where the end of a line is denoted by the two
#   characters CR LF (ASCII 13 10).
#   We use this for symmetry; data processed in the following way:
#       cat | urlencode -l | urldecode -l
#   should (and will) result in the original data
#
#    o  Large lines (or binary files) will break many AWK
#       implementations. If you get the message
#       awk: record `...' too long
#        record number xxx
#   consider using GNU AWK (gawk).
#
#    o  urlencode will always terminate it's output with an EOL
#       character
#
# Thanks to Stefan Brozinski for pointing out a bug related to non-standard
# locales.
#
# See also
#   urldecode
##########################################################################

PN=`basename "$0"`          # Program name
VER='1.4'

: ${AWK=awk}

Usage () {
    echo >&2 "$PN - encode URL data, $VER
usage: $PN [-l] [file ...]
    -l:  encode line endings (result will be one line of output)

The default is to encode each input line on its own."
    exit 1
}

Msg () {
    for MsgLine
    do echo "$PN: $MsgLine" >&2
    done
}

Fatal () { Msg "$@"; 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 ""
    }
' "$@"
MatthieuP
źródło
Czy istnieje prosta odmiana, aby uzyskać kodowanie UTF-8 zamiast ASCII?
avgvstvs
15

To może być najlepszy:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
chenzhiwei
źródło
Działa to dla mnie z dwoma dodatkami: 1. zamień -e na -n, aby uniknąć dodania nowej linii na końcu argumentu i 2. dodaj „%%” do łańcucha printf, aby umieścić% przed każdą parą cyfry szesnastkowe.
Rob Fagen
działa po dodaniu przedziału $ after=$(echo -e ...
forward
1
Wyjaśnij, jak to działa. odPolecenie nie jest powszechne.
Mark Stosberg,
To nie działa z OS X, odponieważ używa innego formatu wyjściowego niż GNU od. Na przykład printf aa|od -An -tx1 -v|tr \ -drukuje -----------61--61--------------------------------------------------------w systemie OS X odi -61-61GNU od. Możesz używać od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\nz OS X odlub GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nrobi to samo, mimo że xxdnie ma go w POSIX, ale odjest.
nisetama
2
Chociaż to może zadziałać, ucieka każdej postaci
Charlie
11

Oto rozwiązanie Bash, które nie wywołuje żadnych programów zewnętrznych:

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"
}
davidchambers
źródło
4
To zachowuje się inaczej między wersjami bash. Na RHEL 6.9 bash to 4.1.2 i zawiera pojedyncze cudzysłowy. Podczas gdy Debian 9 i bash 4.4.12 są w porządku z pojedynczymi cytatami. Dla mnie usunięcie pojedynczych cytatów sprawiło, że zadziałało na obu. s = "$ {s // ',' /% 2C}"
muni764
1
Zaktualizowałem odpowiedź, aby odzwierciedlić twoje odkrycie, @ muni764.
davidchambers
Tylko ostrzeżenie ... to nie á
zakoduje
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')

spowoduje to zakodowanie ciągu wewnątrz $ 1 i wyprowadzenie go w $ url. chociaż nie musisz umieszczać go w var, jeśli chcesz. BTW nie uwzględnił sed dla tabu, sądząc, że zamieni go w spacje

manoflinux
źródło
5
Mam wrażenie, że nie jest to zalecany sposób na zrobienie tego.
Cody Gray
2
wyjaśnij swoje odczucia, proszę .... ponieważ to, co powiedziałem, działa i użyłem go w kilku skryptach, więc wiem, że działa na wszystkie wymienione znaki. więc proszę wyjaśnij, dlaczego ktoś nie używałby mojego kodu i używał perla, ponieważ jego tytuł to „URLEncode ze skryptu bash”, a nie skrypt perla.
manoflinux
czasami nie jest potrzebne żadne rozwiązanie perłowe, więc może się przydać
Yuval Rimar
3
Nie jest to zalecany sposób, aby to zrobić, ponieważ czarna lista to zła praktyka, a i tak jest to nieprzyjazna dla Unicode.
Ekevoo,
To było najbardziej przyjazne rozwiązanie kompatybilne z cat file.txt
mrwaim
7

Dla tych z Was, którzy szukają rozwiązania, które nie potrzebuje perla, oto takie, które potrzebuje tylko hexdump i 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
   '`
}

Połączone ze sobą z kilku miejsc w sieci i trochę lokalnych prób i błędów. Działa świetnie!

Louis Marascio
źródło
7

uni2ascii jest bardzo przydatny:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
kev
źródło
2
To nie działa dla znaków wewnątrz zakresu ASCII, że potrzeby cytowania, jak %i przestrzeń (ta ostatnia może być usunięta z -sflagą)
Boldewyn
7

Jeśli nie chcesz polegać na Perlu, możesz także użyć sed. To trochę bałagan, ponieważ każda postać musi być uciekana indywidualnie. Utwórz plik o następującej treści i wywołaj gourlencode.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

Aby go użyć, wykonaj następujące czynności.

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"

Spowoduje to podzielenie łańcucha na część, która wymaga kodowania, a część, która jest w porządku, koduje część, która jej potrzebuje, a następnie ponownie łączy.

Możesz to dla wygody umieścić w skrypcie sh, może wymagać kodowania parametru, umieścić go na swojej ścieżce, a następnie możesz po prostu wywołać:

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

źródło

Sójka
źródło
7

Możesz emulować javascript encodeURIComponentw perlu. Oto polecenie:

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

Możesz ustawić to jako alias bash w .bash_profile:

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

Teraz możesz przesyłać do encodeURIComponent:

$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!
Klaus
źródło
6

Oto wersja węzła:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\\'}')"
}
davidchambers
źródło
1
Czy to nie zostanie przerwane, jeśli w ciągu są inne znaki, które nie są poprawne między pojedynczymi cudzysłowami, takie jak pojedynczy ukośnik odwrotny lub znaki nowej linii?
Stuart P. Bentley
Słuszna uwaga. Jeśli mamy problem z ucieczką od wszystkich problematycznych postaci w Bash, równie dobrze możemy wykonać zamiany bezpośrednio i nodecałkowicie uniknąć . Opublikowałem rozwiązanie tylko dla Bash. :)
davidchambers
1
Ten wariant znaleziony gdzie indziej na stronie pozwala uniknąć problemu cytowania, odczytując wartość z STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg,
6

Pytanie dotyczy robienia tego w bashu i nie ma potrzeby używania Pythona lub Perla, ponieważ w rzeczywistości istnieje jedno polecenie, które robi dokładnie to, co chcesz - „urlencode”.

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

Jest to również znacznie lepsze, ponieważ powyższy perl na przykład nie koduje poprawnie wszystkich znaków. Wypróbuj to, używając długiej kreski, którą otrzymujesz z Worda, a otrzymasz nieprawidłowe kodowanie.

Uwaga: aby udostępnić to polecenie, musisz zainstalować „klientów gridsite-klientów”.

Dylan
źródło
1
Moja wersja bash (GNU 3.2) nie ma urlencode. Jakiej wersji używasz?
Sridhar Sarnobat,
1
Mam 4.3.42, ale polecenie urlencode jest dostarczane przez „gridsite-client”. Spróbuj zainstalować, a wszystko będzie dobrze.
Dylan,
5
Więc twoja odpowiedź nie jest lepsza niż żadna, która wymaga zainstalowania innych rzeczy (python, perl, lua,…)
Cyrille Pontvieux
Tyle, że wymaga tylko zainstalowania jednego narzędzia zamiast całego języka (i bibliotek), a ponadto jest bardzo prosty i przejrzysty, aby zobaczyć, co robi.
Dylan
Przydatny byłby najpierw link do strony pakietu / projektu udostępniający to polecenie.
Doron Behar
6

Prosta opcja PHP:

echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'
Ryan
źródło
4

Ruby, dla kompletności

value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"
k107
źródło
4

Inne podejście php:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
Jan Halfar
źródło
2
echododa znak nowej linii (hex 0xa). Aby temu zapobiec, użyj echo -n.
Mathew Hall,
3

Oto moja wersja dla jesshell busy dla systemu wbudowanego, pierwotnie przyjąłem wariant 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}"
}
światło nocne
źródło
2

Oto funkcja POSIX, aby to zrobić:

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

Przykład:

value=$(encodeURIComponent "$2")

Źródło

Steven Penny
źródło
2

Oto konwersja jednowierszowa przy użyciu Lua, podobna do odpowiedzi blueyed, z wyjątkiem wszystkich niezarezerwowanych znaków RFC 3986 niezakodowanych (jak ta odpowiedź ):

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

Ponadto może być konieczne upewnienie się, że znaki nowego wiersza w ciągu są konwertowane z LF na CRLF, w którym to przypadku można wstawić gsub("\r?\n", "\r\n")łańcuch przed kodowaniem procentowym.

Oto wariant, który w niestandardowym stylu application / x-www-form-urlencoded wykonuje normalizację nowej linii, a także koduje spacje jako „+” zamiast „% 20” (które prawdopodobnie można by dodać do Fragment kodu Perla przy użyciu podobnej techniki).

url=$(echo 'print((arg[1]:gsub("\r?\n", "\r\n"):gsub("([^%w%-%.%_%~ ]))",function(c)return("%%%02X"):format(c:byte())end):gsub(" ","+"))' | lua - "$1")
Stuart P. Bentley
źródło
1

Po zainstalowaniu php używam w ten sposób:

URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"`
ajaest
źródło
1

To jest wersja ksh odpowiedzi orwellophile zawierającej funkcje rawurlencode i rawurldecode (link: Jak urlencode dane dla polecenia curl? ). Nie mam wystarczającej liczby przedstawicieli, aby opublikować komentarz, stąd nowy post ..

#!/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++
Ray Burgemeestre
źródło
1

Co lepiej parsowałoby adresy URL niż javascript?

node -p "encodeURIComponent('$url')"
Nestor Urquiza
źródło
Zakres poza pytaniem. Nie bash, nie curl. Nawet jeśli jestem pewien, że działa bardzo dobrze, jeśli węzeł jest dostępny.
Cyrille Pontvieux
Po co głosować w dół za tym, a nie za python / perl? Ponadto, w jaki sposób nie odpowiada to pierwotne pytanie „Jak urlencode dane dla polecenia curl?”. Można tego użyć ze skryptu bash, a wynik można podać w poleceniu curl.
Nestor Urquiza
Głosowałem również za innymi. Pytanie brzmiało, jak to zrobić w skrypcie bash. Jeśli używany jest inny język, taki jak node / js, python lub perl, wówczas nie ma potrzeby bezpośredniego używania curl.
Cyrille Pontvieux,
2
Chociaż nie zadałem sobie trudu, by przegłosować, problem z tym poleceniem polega na tym, że wymaga poprawnego ucieczki danych do użycia w javascript. Spróbuj tego z pojedynczymi cudzysłowami i szaleństwem z odwrotnym ukośnikiem. Jeśli chcesz użyć węzła, lepiej czytaj rzeczy ze standardowego node -p 'encodeURIComponent(require("fs").readFileSync(0))'
interfejsu,
1
Zachowaj ostrożność przy rozwiązaniu @ MichaelKrelin-hacker, jeśli przesyłasz dane ze STDIN, pamiętaj, aby nie dołączać końcowego nowego wiersza. Na przykład echo | ...jest zły, a echo -n | ...pomija znak nowej linii.
Mark Stosberg,
0

Poniższe informacje oparte są na odpowiedzi Orwellophila, ale rozwiązują wielobajtowy błąd wymieniony w komentarzach, ustawiając LC_ALL = C (lewę z vte.sh). Napisałem to w formie funkcji odpowiedniej PROMPT_COMMAND, ponieważ właśnie tak go używam.

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}"
}
Per Bothner
źródło