Bagaimana cara urlencode data untuk perintah curl?

319

Saya mencoba menulis skrip bash untuk pengujian yang mengambil parameter dan mengirimkannya melalui curl ke situs web. Saya perlu url mengkodekan nilai untuk memastikan bahwa karakter khusus diproses dengan benar. Apa cara terbaik untuk melakukan ini?

Berikut ini skrip dasar saya:

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

Jawaban:

396

Gunakan curl --data-urlencode; dari man curl:

Ini memposting data, mirip dengan --dataopsi lain dengan pengecualian bahwa ini melakukan pengkodean URL. Agar sesuai dengan CGI, <data>bagian tersebut harus dimulai dengan nama diikuti oleh pemisah dan spesifikasi konten.

Contoh penggunaan:

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

Lihat halaman manual untuk info lebih lanjut.

Ini membutuhkan curl 7.18.0 atau lebih baru (dirilis Januari 2008) . Gunakan curl -Vuntuk memeriksa versi yang Anda miliki.

Anda juga dapat menyandikan string kueri :

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
sumber
5
Tampaknya hanya berfungsi untuk http POST. Dokumentasi di sini: curl.haxx.se/docs/manpage.html#--data-urlencode
Stan James
82
@StanJames Jika Anda menggunakannya seperti itu, ikal juga dapat melakukan penyandian untuk permintaan GET. curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
kberg
13
@kberg sebenarnya, ini hanya akan berfungsi untuk data kueri. ikal akan menambahkan '?' diikuti oleh parl urlencode. Jika Anda ingin urlencode beberapa postfix url (seperti CouchDB GET untuk beberapa id dokumen), maka '--data-urlencode' tidak akan berfungsi.
Bokeh
1
Tidak berhasil curl --data-urlencode "description=![image]($url)" www.example.com. Ada yang tahu kenapa? `
Khurshid Alam
1
@NadavB Lolos "dari‽
BlackJack
179

Ini jawaban murni 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
}

Anda dapat menggunakannya dalam dua cara:

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

[diedit]

Inilah fungsi rawurldecode () yang cocok, yang - dengan segala kerendahan hati - mengagumkan.

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

Dengan set yang cocok, kami sekarang dapat melakukan beberapa tes sederhana:

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

Output: Matched

Dan jika Anda benar-benar merasa bahwa Anda memerlukan alat eksternal (well, itu akan jauh lebih cepat, dan mungkin melakukan file biner dan semacamnya ...) Saya menemukan ini di router OpenWRT saya ...

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

Di mana url_escape.sed adalah file yang berisi aturan-aturan ini:

# 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
sumber
4
Sayangnya, skrip ini gagal pada beberapa karakter, seperti 'é' dan '½', menghasilkan 'e% FFFFFFFFFFFFFFCC' dan '% FFFFFFFFFFFFFFC2', masing-masing (b / c dari loop per karakter, saya percaya).
Matthemattics
1
Gagal untuk saya di Bash 4.3.11 (1). String Jogging «à l'Hèze»menghasilkan Jogging%20%abà%20l%27Hèze%bbyang tidak bisa diumpankan ke JS decodeURIComponent:(
dmcontador
2
Dalam blok kode pertama itu apa arti parameter terakhir untuk printf? Yaitu, mengapa itu double-quote, single-quote, dollar-sign, letter-c, double-quote? Apakah yang dilakukan oleh single-quote?
Colin Fraizer
1
@dmcontador - ini hanya skrip bash sederhana, tidak memiliki konsep multi-byte karakter, atau unicode. Ketika melihat karakter seperti ń ( \u0144) ia akan menampilkan% 144 secara naif, ╡ ( \u2561) akan menjadi keluaran sebagai% 2561. Jawaban rawurlencoded yang benar untuk masing-masing adalah% C5% 84% 0A dan% E2% 95% A1.
Orwellophile
1
@ColinFraizer, kutipan tunggal berfungsi untuk mengubah karakter berikut menjadi nilai numeriknya. ref. pubs.opengroup.org/onlinepubs/9699919799/utilities/...
Sam
94

Gunakan URI::Escapemodul dan uri_escapefungsi Perl di baris kedua skrip bash Anda:

...

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

Sunting: Perbaiki masalah mengutip, seperti yang disarankan oleh Chris Johnsen dalam komentar. Terima kasih!

dubek
sumber
2
URI :: Escape mungkin tidak diinstal, periksa jawaban saya dalam kasus itu.
blueyed
Saya memperbaiki ini (gunakan echo, pipa dan <>), dan sekarang berfungsi bahkan ketika $ 2 berisi tanda kutip atau tanda kutip ganda. Terima kasih!
dubek
9
Anda melakukannya echojuga:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen
1
Versi Chris Johnsen lebih baik. Saya memiliki $ {True} dalam ekspresi pengujian saya dan menggunakan ini melalui gema tersandung ekspansi variabel uri_escape / Perl.
mm2001
1
@ jrw32982 ya, melihat kembali, memiliki bahasa lain yang dapat digunakan untuk menyelesaikan tugas ini adalah baik. Jika saya bisa, saya akan mengambil kembali downvote saya, tetapi sayangnya itu saat ini terkunci.
thecoshman
69

Pilihan lain adalah menggunakan jq(sebagai filter):

jq -sRr @uri

-R( --raw-input) memperlakukan jalur input sebagai string alih-alih menguraikannya sebagai JSON dan -sR( --slurp --raw-input) membaca input menjadi string tunggal. -r(--raw-output ) menampilkan isi string bukannya string literal JSON.

Jika input bukan output dari perintah lain, Anda dapat menyimpannya dalam jqvariabel string:

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

-n( --null-input) tidak membaca input, dan --arg name valuemenyimpan valuedalam variabel namesebagai string. Di filter, $name(dalam tanda kutip tunggal, untuk menghindari ekspansi oleh shell), referensi variabel name.

Dibungkus sebagai fungsi Bash, ini menjadi:

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

Atau ini persen-encode semua byte:

xxd -p|tr -d \\n|sed 's/../%&/g'
nisetama
sumber
3
<3 itu ... harus menjadi IMO teratas & diterima (ya jika Anda bisa mengatakan curluntuk menyandikan yang berfungsi dan jika bash memiliki builtin yang akan diterima - tetapi jqsepertinya pas untuk saya, saya masih jauh dari mencapai tingkat kenyamanan dengan alat ini)
nhed
5
bagi siapa pun yang bertanya hal yang sama seperti saya: @uribukan beberapa variabel, tetapi filter jq literal yang digunakan untuk memformat string dan melarikan diri; lihat manual jq untuk detail (maaf, tidak ada tautan langsung, perlu mencari @uridi halaman ...)
ssc
versi xxd adalah jenis hal yang saya cari. Meskipun sedikit kotor, pendek dan tidak memiliki ketergantungan
Rian Sanderson
1
Contoh penggunaan jq untuk menyandi-url:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal
67

demi kelengkapan, banyak solusi menggunakan sedatau awkhanya menerjemahkan serangkaian karakter khusus dan karenanya cukup besar berdasarkan ukuran kode dan juga jangan menerjemahkan karakter khusus lain yang harus dikodekan.

cara aman untuk urlencode adalah dengan hanya menyandikan setiap byte - bahkan yang sudah diizinkan.

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

xxd berhati-hati di sini bahwa input ditangani sebagai byte dan bukan karakter.

edit:

xxd hadir dengan paket vim-common di Debian dan saya hanya menggunakan sistem yang tidak diinstal dan saya tidak ingin menginstalnya. Altornative adalah menggunakanhexdump dari paket bsdmainutils di Debian. Menurut grafik berikut, bsdmainutils dan vim-common harus memiliki kemungkinan yang hampir sama untuk diinstal:

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

namun demikian di sini versi yang menggunakan hexdumpalih-alih xxddan memungkinkan untuk menghindari trpanggilan:

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
josch
sumber
1
xxd -plainharus terjadi SETELAH tr -d '\n'!
qdii
3
@qdii kenapa? yang tidak hanya akan membuat urlencode baris baru tidak mungkin tetapi juga akan memasukkan baris baru yang dibuat oleh xxd ke dalam output.
josch
1
@josch. Ini benar-benar salah. Pertama, setiap \nkarakter akan diterjemahkan oleh xxd -plainke 0a. Jangan mengambil kata-kata saya untuk itu, coba sendiri: echo -n -e '\n' | xxd -plainIni membuktikan bahwa Anda tr -d '\n'tidak berguna di sini karena tidak mungkin ada \nsetelah xxd -plain Kedua, echo foobartambahkan \nkarakter sendiri di akhir string karakter, jadi xxd -plainjangan diberi makan foobarseperti yang diharapkan tetapi dengan foobar\n. kemudian xxd -plain menerjemahkannya ke beberapa string karakter yang berakhir 0a, membuatnya tidak cocok untuk pengguna. Anda bisa menambahkan -nuntuk echomengatasinya.
qdii
6
@qdii memang -n tidak ada untuk gema tetapi xxdpanggilan itu ada di depan tr -dpanggilan. Itu milik di sana sehingga setiap baris baru dalam foobarditerjemahkan oleh xxd. The tr -dsetelah xxdpanggilan untuk menghapus baris yang xxd menghasilkan. Tampaknya Anda tidak pernah memiliki foobar cukup lama sehingga xxdmenghasilkan baris baru tetapi untuk input panjang itu akan. Jadi tr -ditu perlu. Berbeda dengan asumsi Anda, tr -dBUKAN untuk menghapus baris baru dari input tetapi dari xxdoutput. Saya ingin menyimpan baris baru di input. Satu-satunya poin Anda yang valid adalah, gema itu menambahkan baris baru yang tidak perlu.
josch
1
@qdii dan tidak ada pelanggaran yang dilakukan - Saya hanya berpikir bahwa Anda salah, kecuali echo -nyang memang saya lewatkan
josch
62

Salah satu varian, mungkin jelek, tetapi sederhana:

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
}

Berikut ini adalah versi satu-liner misalnya (seperti yang disarankan oleh 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/'
Sergey
sumber
1
Saya pikir ini adalah cara yang sangat cerdas untuk menggunakan kembali pengkodean URL CURL.
solidsnack
13
Ini benar-benar brilian! Saya benar-benar berharap Anda telah meninggalkannya satu baris sehingga orang dapat melihat betapa sederhananya itu sebenarnya. Untuk URL menyandikan hasil dari dateperintah ... date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(Anda harus cutmematikan 2 karakter pertama, karena output curl adalah secara teknis URL relatif dengan string kueri.)
Bruno Bronosky
2
@BrunoBronosky Varian satu liner Anda baik tetapi sepertinya menambahkan "% 0A" di akhir penyandian. Pengguna berhati-hatilah. Versi fungsi tampaknya tidak memiliki masalah ini.
levigroker
7
Untuk menghindari %0Adi akhir, gunakan printfsebagai ganti echo.
kenorb
2
the one liner is fantastic
Stephen Blum
49

Saya merasa lebih mudah dibaca dengan python:

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

triple 'memastikan bahwa tanda kutip nilai tidak akan merugikan. urllib ada di perpustakaan standar. Ini berfungsi sebagai contoh untuk url (dunia nyata) yang gila ini:

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
sandro
sumber
2
Saya mengalami beberapa masalah dengan tanda kutip dan karakter khusus dengan triplequoting, ini sepertinya bekerja untuk semuanya: encoded_value = "$ (echo -n" $ {data} "| python -c" import urllib; import urllib; import sys; sys.stdout. tulis (urllib.quote (sys.stdin.read ())) ")";
Berhenti memfitnah Monica Cellio
Versi Python 3 akan menjadi 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)'hampir tidak memiliki masalah mengutip, dan harus efisien dalam memori / kecepatan (belum diperiksa, simpan untuk menyipitkan mata)
Alois Mahdal
2
Akan jauh lebih aman untuk merujuk sys.argvdaripada mengganti $valueke string yang kemudian diuraikan sebagai kode. Bagaimana jika valueterkandung ''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy
2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite
30

Saya telah menemukan potongan berikut berguna untuk memasukkannya ke dalam rangkaian panggilan program, di mana URI :: Escape mungkin tidak diinstal:

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

( sumber )

kebiru-biruan
sumber
4
bekerja untukku. Saya mengubahnya menjadi perl-lpe ... (huruf ell). Ini menghapus baris tambahan, yang saya butuhkan untuk tujuan saya.
JohnnyLambada
2
FYI, untuk melakukan kebalikan dari ini, gunakan perl -pe 's/\%(\w\w)/chr hex $1/ge'(sumber: unix.stackexchange.com/questions/159253/… )
Sridhar Sarnobat
2
Bergantung pada karakter mana yang perlu Anda enkode, Anda dapat menyederhanakan ini perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'yang memungkinkan huruf, angka, dan garis bawah, tetapi menyandikan yang lainnya.
robru
23

Jika Anda ingin menjalankan GETpermintaan dan menggunakan ikal murni, tambahkan saja --getke solusi @ Jacob.

Berikut ini sebuah contoh:

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

Tautan langsung ke versi awk: http://www.shelldorado.com/scripts/cmds/urlencode
Saya menggunakannya selama bertahun-tahun dan berfungsi seperti pesona

:
##########################################################################
# 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
sumber
Apakah ada variasi sederhana untuk mendapatkan pengkodean UTF-8 dan bukan ASCII?
avgvstvs
15

Ini mungkin yang terbaik:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
chenzhiwei
sumber
Ini berfungsi untuk saya dengan dua tambahan: 1. ganti -e dengan -n untuk menghindari menambahkan baris baru ke akhir argumen dan 2. menambahkan '%%' ke string printf untuk meletakkan% di depan setiap pasang digit hex
Rob Fagen
berfungsi setelah menambahkan braket $ depan after=$(echo -e ...
Roman Rhrn Nesterov
1
Tolong jelaskan bagaimana ini bekerja. The odperintah tidak umum.
Mark Stosberg
Ini tidak berfungsi dengan OS X odkarena menggunakan format output yang berbeda dari GNU od. Misalnya printf aa|od -An -tx1 -v|tr \ -mencetak -----------61--61--------------------------------------------------------dengan OS X oddan -61-61dengan GNU od. Anda dapat menggunakannya od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\ndengan OS X odatau GNU od. xxd -p|sed 's/../%&/g'|tr -d \\nmelakukan hal yang sama, meskipun xxdtidak dalam POSIX tetapi od.
nisetama
2
Meskipun ini mungkin berhasil, itu lolos dari setiap karakter tunggal
Charlie
11

Inilah solusi Bash yang tidak menjalankan program eksternal apa pun:

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
sumber
4
Ini berperilaku berbeda antara versi bash. Pada RHEL 6.9 bash adalah 4.1.2 dan itu termasuk tanda kutip tunggal. Sementara Debian 9 dan bash 4.4.12 tidak masalah dengan tanda kutip tunggal. Bagi saya menghapus tanda kutip tunggal membuatnya bekerja pada keduanya. s = "$ {s // ',' /% 2C}"
muni764
1
Saya memperbarui jawaban untuk mencerminkan temuan Anda, @ muni764.
davidchambers
Hanya peringatan ... ini tidak akan menyandikan hal-hal seperti karakterá
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')

ini akan menyandikan string di dalam $ 1 dan menghasilkannya dalam $ url. meskipun Anda tidak harus memasukkannya ke dalam var jika Anda mau. BTW tidak termasuk sed untuk tab pikir itu akan mengubahnya menjadi spasi

manoflinux
sumber
5
Saya merasa ini bukan cara yang disarankan untuk melakukan ini.
Cody Grey
2
tolong jelaskan perasaan Anda .... karena saya apa yang telah saya nyatakan berhasil dan saya telah menggunakannya dalam beberapa skrip jadi saya tahu itu berfungsi untuk semua karakter yang saya daftarkan. jadi tolong jelaskan mengapa seseorang tidak akan menggunakan kode saya dan menggunakan perl karena judul ini adalah "URLEncode dari skrip bash" bukan skrip perl.
manoflinux
terkadang tidak diperlukan solusi mutiara sehingga ini bisa berguna
Yuval Rimar
3
Ini bukan cara yang disarankan untuk melakukan ini karena daftar hitam adalah praktik yang buruk, dan ini juga bukan unicode.
Ekevoo
Ini adalah solusi paling ramah yang kompatibel dengan cat
file.txt
7

Bagi Anda yang mencari solusi yang tidak membutuhkan perl, berikut ini adalah yang hanya membutuhkan hexdump dan 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
   '`
}

Dijahit bersama dari beberapa tempat di internet dan beberapa percobaan dan kesalahan lokal. Ini sangat bagus!

Louis Marascio
sumber
7

uni2ascii sangat berguna:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
kev
sumber
2
Ini tidak berfungsi untuk karakter di dalam rentang ASCII, yang perlu mengutip, suka, %dan spasi (yang terakhir dapat diperbaiki dengan -sbendera)
Boldewyn
7

Jika Anda tidak ingin bergantung pada Perl, Anda juga dapat menggunakan sed. Agak berantakan, karena setiap karakter harus melarikan diri secara individual. Buat file dengan konten berikut dan panggil ituurlencode.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

Untuk menggunakannya lakukan hal berikut.

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"

Ini akan membagi string menjadi bagian yang perlu dikodekan, dan bagian yang baik-baik saja, mengkodekan bagian yang membutuhkannya, lalu menjahit kembali menjadi satu.

Anda dapat memasukkannya ke dalam skrip sh untuk kenyamanan, mungkin memerlukannya mengambil parameter untuk menyandikan, meletakkannya di jalur Anda dan kemudian Anda bisa memanggil:

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

sumber

Jay
sumber
7

Anda dapat meniru javascript encodeURIComponentdalam perl. Inilah perintahnya:

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

Anda dapat mengatur ini sebagai alias bash di .bash_profile:

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

Sekarang Anda dapat mengirim pipa ke encodeURIComponent:

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

Inilah versi simpulnya:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\\'}')"
}
davidchambers
sumber
1
Tidakkah ini akan pecah jika ada karakter lain dalam string yang tidak valid di antara tanda kutip tunggal, seperti garis miring terbalik tunggal, atau baris baru?
Stuart P. Bentley
Poin yang bagus. Jika kita ingin keluar dari kesulitan untuk menghilangkan semua karakter bermasalah di Bash, kita bisa melakukan penggantian secara langsung dan menghindari nodesemuanya. Saya memposting solusi Bash-only. :)
davidchambers
1
Varian ini ditemukan di tempat lain pada halaman menghindari masalah mengutip dengan membaca nilai dari STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg
6

Pertanyaannya adalah tentang melakukan ini di bash dan tidak perlu untuk python atau perl karena sebenarnya ada satu perintah yang melakukan apa yang Anda inginkan - "urlencode".

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

Ini juga jauh lebih baik, karena jawaban perl di atas, misalnya, tidak menyandikan semua karakter dengan benar. Cobalah dengan tanda hubung panjang yang Anda dapatkan dari Word dan Anda mendapatkan kode yang salah.

Catatan, Anda perlu "gridsite-clients" diinstal untuk memberikan perintah ini.

Dylan
sumber
1
Versi bash saya (GNU 3.2) tidak punya urlencode. Versi apa yang Anda gunakan?
Sridhar Sarnobat
1
Saya memiliki 4.3.42, tetapi perintah urlencode disediakan oleh "gridsite-clients". Coba pasang itu dan Anda akan baik-baik saja.
Dylan
5
Jadi jawaban Anda tidak lebih baik daripada yang meminta orang lain memasang (python, perl, lua, ...)
Cyrille Pontvieux
Kecuali itu hanya membutuhkan menginstal utilitas tunggal alih-alih seluruh bahasa (dan perpustakaan), plus sangat sederhana dan jelas untuk melihat apa yang dilakukannya.
Dylan
Tautan pertama untuk halaman paket / proyek yang menyediakan perintah ini akan bermanfaat.
Doron Behar
6

Opsi PHP sederhana:

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

Ruby, untuk kelengkapan

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

Pendekatan php lain:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
jan halfar
sumber
2
echoakan menambahkan karakter baris baru (hex 0xa). Untuk berhenti melakukannya, gunakan echo -n.
Mathew Hall
3

Ini adalah versi saya untuk shell ash busybox untuk sistem tertanam, saya awalnya mengadopsi varian 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}"
}
nulleight
sumber
2

Berikut adalah fungsi POSIX untuk melakukan itu:

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

Contoh:

value=$(encodeURIComponent "$2")

Sumber

Steven Penny
sumber
2

Berikut ini adalah konversi satu baris menggunakan Lua, mirip dengan jawaban blueyed kecuali dengan semua RFC 3986 Karakter yang Tidak Ditangguhkan dibiarkan tidak tersandi (seperti jawaban ini ):

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

Selain itu, Anda mungkin perlu memastikan bahwa baris baru di string Anda dikonversi dari LF ke CRLF, dalam hal ini Anda bisa memasukkan a gsub("\r?\n", "\r\n")dalam rantai sebelum pengkodean persen.

Inilah varian yang, dalam gaya aplikasi / x-www-form-urlencoded yang tidak standar, apakah itu normalisasi baris baru, serta ruang enkode sebagai '+' alih-alih '% 20' (yang mungkin dapat ditambahkan ke Cuplikan Perl menggunakan teknik serupa).

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
sumber
1

Setelah menginstal php saya menggunakan cara ini:

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

Ini adalah versi ksh dari jawaban orwellophile yang berisi fungsi rawurlencode dan rawurldecode (tautan: Bagaimana cara urlencode data untuk perintah curl? ). Saya tidak punya cukup perwakilan untuk memposting komentar, karenanya posting baru ..

#!/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
sumber
1

Apa yang akan mem-parsing URL lebih baik daripada javascript?

node -p "encodeURIComponent('$url')"
Nestor Urquiza
sumber
Di luar lingkup pertanyaan op. Bukan bash, tidak curl. Bahkan jika saya yakin bekerja dengan sangat baik jika node tersedia.
Cyrille Pontvieux
Mengapa memilih ini dan bukan jawaban python / perl? Selanjutnya bagaimana ini tidak menjawab pertanyaan asli "Bagaimana cara urlencode data untuk perintah curl?". Ini dapat digunakan dari skrip bash dan hasilnya dapat diberikan ke perintah curl.
Nestor Urquiza
Saya memilih orang lain juga. Pertanyaannya adalah bagaimana melakukan ini dalam skrip bash. Jika bahasa lain digunakan seperti node / js, python atau perl, maka tidak perlu menggunakan curl secara langsung.
Cyrille Pontvieux
2
Meskipun saya tidak repot-repot untuk downvote, masalah dengan perintah ini adalah bahwa ia membutuhkan data untuk melarikan diri dengan benar untuk digunakan dalam javascript. Seperti mencobanya dengan tanda kutip tunggal dan beberapa kegilaan backslash. Jika Anda ingin menggunakan node, Anda sebaiknya membaca hal-hal dari stdin sepertinode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - hacker
1
Hati-hati dengan solusi @ MichaelKrelin-hacker jika Anda mengirim data dari STDIN, pastikan untuk tidak menyertakan baris baru yang tertinggal. Misalnya, echo | ...salah, sementara echo -n | ...menekan baris baru.
Mark Stosberg
0

Berikut ini didasarkan pada jawaban Orwellophile, tetapi memecahkan bug multibyte yang disebutkan dalam komentar dengan mengatur LC_ALL = C (trik dari vte.sh). Saya telah menulisnya dalam bentuk fungsi yang sesuai PROMPT_COMMAND, karena itulah cara saya menggunakannya.

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
sumber