วิธีการ urlencode ข้อมูลสำหรับคำสั่ง curl


319

ฉันพยายามเขียน bash script สำหรับการทดสอบที่รับพารามิเตอร์และส่งผ่านทาง curl ไปยังเว็บไซต์ ฉันจำเป็นต้องเข้ารหัสค่าเพื่อให้แน่ใจว่ามีการประมวลผลอักขระพิเศษอย่างถูกต้อง วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร?

นี่คือสคริปต์พื้นฐานของฉันจนถึง:

#!/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
Stan James

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 จะต่อท้าย '?' ตามด้วย params แบบ urlencoded หากคุณต้องการ urlencode postfix url บางส่วน (เช่น CouchDB GET สำหรับ id เอกสารบางอย่าง) ดังนั้น '--data-urlencode' จะไม่ทำงาน
Bokeh

1
curl --data-urlencode "description=![image]($url)" www.example.comไม่ทำงานสำหรับ มีความคิดอะไรไหม? `
Khurshid Alam

1
@NadavB กำลังหลบหนี"
BlackJack

179

นี่คือคำตอบ BASH บริสุทธิ์

rawurlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"    # You can either set a return variable (FASTER) 
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

คุณสามารถใช้ได้สองวิธี:

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

[แก้ไข]

นี่คือฟังก์ชั่น rawurldecode () ที่ตรงกันซึ่งด้วยความสุภาพเรียบร้อยยอดเยี่ยม

# Returns a string in which the sequences with percent (%) signs followed by
# two hex digits have been replaced with literal characters.
rawurldecode() {

  # This is perhaps a risky gambit, but since all escape characters must be
  # encoded, we can replace %NN with \xNN and pass the lot to printf -b, which
  # will decode hex for us

  printf -v REPLY '%b' "${1//%/\\x}" # You can either set a return variable (FASTER)

  echo "${REPLY}"  #+or echo the result (EASIER)... or both... :p
}

ด้วยชุดการจับคู่ตอนนี้เราสามารถทำการทดสอบง่ายๆ:

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

Output: Matched

และถ้าคุณรู้สึกว่าคุณต้องการเครื่องมือภายนอกจริง ๆ (มันจะไปเร็วขึ้นมากและอาจทำไฟล์ไบนารี่และ ... ) ฉันพบสิ่งนี้ในเราเตอร์ OpenWRT ของฉัน ...

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

โดยที่ url_escape.sed เป็นไฟล์ที่มีกฎเหล่านี้:

# sed url escaping
s:%:%25:g
s: :%20:g
s:<:%3C:g
s:>:%3E:g
s:#:%23:g
s:{:%7B:g
s:}:%7D:g
s:|:%7C:g
s:\\:%5C:g
s:\^:%5E:g
s:~:%7E:g
s:\[:%5B:g
s:\]:%5D:g
s:`:%60:g
s:;:%3B:g
s:/:%2F:g
s:?:%3F:g
s^:^%3A^g
s:@:%40:g
s:=:%3D:g
s:&:%26:g
s:\$:%24:g
s:\!:%21:g
s:\*:%2A:g

4
น่าเสียดายที่สคริปต์นี้ล้มเหลวในอักขระบางตัวเช่น 'é' และ '½' ซึ่งแสดงผล 'e% FFFFFFFFFFFFFFCC' และ '% FFFFFFFFFFFFFFC2' ตามลำดับ (b / c ของลูปต่ออักขระฉันเชื่อว่า)
Matthemattics

1
มันใช้ไม่ได้กับฉันใน Bash 4.3.11 (1) สตริงJogging «à l'Hèze»สร้างJogging%20%abà%20l%27Hèze%bbที่ไม่สามารถฟีดไปยัง JS decodeURIComponent:(
dmcontador

2
ในบล็อกแรกของรหัสพารามิเตอร์สุดท้ายที่จะพิมพ์หมายถึงอะไร นั่นคือเหตุผลที่ว่าทำไมมันเป็น double-quote, single-quote, dollar-sign, letter-c, double-quote? เครื่องหมายคำพูดเดี่ยวทำอะไรได้บ้าง?
Colin Fraizer

1
@dmcontador - เป็นเพียงสคริปต์ทุบตีต่ำต้อยไม่มีความคิดของตัวละครหลายไบต์หรือ unicode เมื่อเห็นว่าเป็นตัวละครเช่นń ( \u0144) มันจะเอาท์พุทอย่างไร้เดียงสา% 144, ╡ ( \u2561) จะถูกส่งออกเป็น% 2561 คำตอบrawurlencoded ที่ถูกต้องสำหรับสิ่งเหล่านี้คือ% C5% 84% 0A และ% E2% 95% A1 ตามลำดับ
Orwellophile

1
@ColinFraizer เครื่องหมายคำพูดเดี่ยวทำหน้าที่แปลงอักขระต่อไปนี้เป็นค่าตัวเลข อ้าง pubs.opengroup.org/onlinepubs/9699919799/utilities/...
แซม

94

ใช้URI::Escapeโมดูลและuri_escapeฟังก์ชั่นของ Perl ในบรรทัดที่สองของสคริปต์ทุบตี:

...

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

แก้ไข:แก้ไขปัญหาการอ้างอิงตามที่ Chris Johnsen แนะนำในความคิดเห็น ขอบคุณ!


2
URI :: ไม่สามารถติดตั้ง Escape ได้ตรวจสอบคำตอบของฉันในกรณีนั้น
blueyed

ฉันแก้ไขสิ่งนี้ (ใช้echoไปป์และ<>) และตอนนี้ก็ใช้ได้แม้ตอนที่ $ 2 จะมีเครื่องหมายอัญประกาศเดี่ยวหรือเครื่องหมายคำพูดคู่ ขอบคุณ!
dubek

9
คุณทำไปด้วยecho:value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen

1
เวอร์ชันของ Chris Johnsen ดีกว่า ฉันมี $ {True} ในการแสดงออกการทดสอบของฉันและใช้สิ่งนี้ผ่าน echo ที่เพิ่มขึ้นการขยายตัวของตัวแปร uri_escape / Perl
mm2001

1
@ jrw32982 ใช่มองย้อนกลับไปมีภาษาอื่นที่ใช้ในการทำภารกิจนี้ได้ดี ถ้าฉันทำได้ฉันก็จะเอา downvote ของฉันกลับมา แต่มันก็ถูกล็อคอยู่ในขณะนี้
thecoshman

69

ตัวเลือกอื่นคือใช้jq(เป็นตัวกรอง):

jq -sRr @uri

-R( --raw-input) ถือว่าบรรทัดอินพุตเป็นสตริงแทนที่จะแยกวิเคราะห์เป็น JSON และ-sR( --slurp --raw-input) อ่านอินพุตเป็นสตริงเดี่ยว -r(--raw-output ) แสดงผลเนื้อหาของสตริงแทนตัวอักษรของสตริง JSON

หากอินพุตไม่ใช่เอาต์พุตของคำสั่งอื่นคุณสามารถเก็บไว้ในjqตัวแปรสตริง:

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

-n( --null-input) ไม่อ่านอินพุตและ--arg name valueเก็บvalueในตัวแปรnameเป็นสตริง ในตัวกรอง$name(ในเครื่องหมายคำพูดเดี่ยวเพื่อหลีกเลี่ยงการขยายโดยเชลล์) อ้างอิงตัวแปรnameอ้างอิงตัวแปร

ห่อเป็นฟังก์ชัน Bash สิ่งนี้จะกลายเป็น:

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

หรือเปอร์เซ็นต์นี้เข้ารหัสไบต์ทั้งหมด:

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

3
<3 มัน ... ควรจะอยู่ด้านบนสุด & เป็นที่ยอมรับ IMO (ใช่ถ้าคุณบอกcurlให้เข้ารหัสที่ใช้งานได้และถ้า bash มี builtin ที่น่าจะยอมรับได้ - แต่jqดูเหมือนว่าจะเหมาะกับคุณฉันอยู่ไกลจากระดับความสะดวกสบายด้วย เครื่องมือนี้)
nhed

5
สำหรับทุกคนที่สงสัยในสิ่งเดียวกันกับฉัน: @uriไม่ใช่ตัวแปร แต่เป็นตัวกรอง jq ที่ใช้สำหรับการจัดรูปแบบสตริงและการหลบหนี ดูคู่มือ jqเพื่อดูรายละเอียด (ขออภัยไม่มีลิงก์โดยตรงจำเป็นต้องค้นหา@uriในหน้า ... )
ssc

รุ่น xxd เป็นเพียงสิ่งที่ฉันกำลังมองหา แม้ว่ามันจะสกปรกเล็กน้อย แต่ก็สั้นและไม่มีการพึ่งพา
Rian Sanderson

1
ตัวอย่างการใช้ jq กับ url-encode:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal

67

เพื่อความสมบูรณ์โซลูชันจำนวนมากใช้sedหรือawkแปลเฉพาะชุดอักขระและจึงมีขนาดค่อนข้างใหญ่ตามขนาดรหัสและอย่าแปลอักขระพิเศษอื่น ๆ ที่ควรเข้ารหัส

วิธีที่ปลอดภัยในการ urlencode คือการเข้ารหัสทุก ๆ ไบต์แม้แต่สิ่งที่จะได้รับอนุญาต

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

xxd กำลังดูแลที่นี่ว่าอินพุตถูกจัดการเป็นไบต์และไม่ใช่อักขระ

แก้ไข:

xxd มาพร้อมกับแพ็คเกจทั่วไปใน Debian และฉันเป็นเพียงระบบที่ไม่ได้ติดตั้งและฉันไม่ต้องการติดตั้ง altornative ใช้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 newlines แต่มันจะแทรกขึ้นบรรทัดใหม่อย่างผิดพลาดโดย xxd ในเอาต์พุต
josch

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 ผลิต ดูเหมือนว่าคุณไม่เคยมี foobar มานานพอที่จะสร้างบรรทัดใหม่ แต่สำหรับอินพุตที่ยาวนาน ดังนั้นสิ่งที่จำเป็น ตรงกันข้ามกับข้อสันนิษฐานของคุณคือการไม่ลบบรรทัดใหม่ออกจากอินพุต แต่ออกจากเอาต์พุต ฉันต้องการเก็บบรรทัดใหม่ในอินพุต จุดที่ถูกต้องของคุณคือ echo ที่เพิ่มบรรทัดใหม่ที่ไม่จำเป็น xxdtr -dxxdxxdtr -dtr -dxxd
josch

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
}

ต่อไปนี้เป็นตัวอย่างแบบหนึ่งซับ (ตามที่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/'

1
ฉันคิดว่านี่เป็นวิธีที่ชาญฉลาดในการใช้การเข้ารหัส URL ของ cURL อีกครั้ง
solidsnack

13
มันยอดเยี่ยมมาก! ฉันหวังว่าคุณจะทิ้งมันไว้หนึ่งบรรทัดเพื่อให้ผู้คนเห็นว่ามันเรียบง่ายเพียงใด ในการเข้ารหัส URL ผลลัพธ์ของdateคำสั่ง… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(คุณต้องcutปิด 2 chars แรกเนื่องจากเอาต์พุตของ curl เป็น URL เชิงเทคนิคที่มีสตริงข้อความค้นหา)
Bruno Bronosky

2
@BrunoBronosky ตัวแปรซับในหนึ่งชุดของคุณดี แต่ดูเหมือนจะเพิ่ม "% 0A" ลงในส่วนท้ายของการเข้ารหัส ผู้ใช้ระวัง รุ่นฟังก์ชั่นดูเหมือนจะไม่มีปัญหานี้
levigroker

7
เพื่อหลีกเลี่ยงการ%0Aที่ปลายใช้แทนprintf echo
kenorb

2
สายการบินหนึ่งที่ยอดเยี่ยม
Stephen Blum

49

ฉันพบว่ามันสามารถอ่านได้มากขึ้นในหลาม:

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" urlib นำเข้า; sys.stdout เขียน (urllib.quote (sys.stdin.read ())) ")";
หยุดการใส่ร้ายโมนิก้า Cellio

งูหลาม 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)'แทบไม่มีปัญหาในการพูดและควรจะมีหน่วยความจำ / ความเร็วที่มีประสิทธิภาพ (ยังไม่ได้ตรวจสอบ, ประหยัดสำหรับการ squinting)
Alois Mahdal

2
มันจะปลอดภัยกว่าในการอ้างถึงsys.argvแทนที่จะใช้การแทนที่$valueเป็นสตริงในภายหลังโดยแยกวิเคราะห์เป็นรหัส เกิดอะไรขึ้นถ้าvalueมี''' + __import__("os").system("rm -rf ~") + '''?
Charles Duffy

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

30

ฉันพบข้อมูลโค้ดต่อไปนี้มีประโยชน์ที่จะติดไว้ในสายการโทรของโปรแกรมซึ่ง URI :: Escape อาจไม่ได้รับการติดตั้ง:

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

(ที่มา )


4
ทำงานให้ฉัน ฉันเปลี่ยนมันเป็น perl -lpe ... (ตัวอักษร ell) นี่เป็นการลบบรรทัดใหม่ที่ตามมาซึ่งฉันต้องการเพื่อวัตถุประสงค์ของฉัน
JohnnyLambada

2
หากต้องการทำสิ่งที่ตรงกันข้ามนี้ให้ใช้perl -pe 's/\%(\w\w)/chr hex $1/ge'(ที่มา: unix.stackexchange.com/questions/159253/ … )
Sridhar Sarnobat

2
คุณสามารถทำให้สิ่งนี้ง่ายขึ้นperl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'ซึ่งอนุญาตให้ตัวอักษรตัวเลขและขีดล่าง แต่เข้ารหัสทุกอย่างอื่นทั้งนี้ขึ้นอยู่กับเฉพาะอักขระที่คุณต้องเข้ารหัส
robru

23

หากคุณต้องการเรียกใช้GETคำขอและใช้ curl แท้ๆเพียงเพิ่ม--getไปยังโซลูชันของ @ Jacob

นี่คือตัวอย่าง:

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

15

ลิงก์โดยตรงไปยังรุ่น awk: http://www.shelldorado.com/scripts/cmds/urlencode
ฉันใช้มันมาหลายปีแล้วและมันใช้งานได้อย่างมีเสน่ห์

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

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

: ${AWK=awk}

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

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

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

Fatal () { Msg "$@"; exit 1; }

set -- `getopt hl "$@" 2>/dev/null` || Usage
[ $# -lt 1 ] && Usage           # "getopt" detected an error

EncodeEOL=no
while [ $# -gt 0 ]
do
    case "$1" in
        -l) EncodeEOL=yes;;
    --) shift; break;;
    -h) Usage;;
    -*) Usage;;
    *)  break;;         # First file name
    esac
    shift
done

LANG=C  export LANG
$AWK '
    BEGIN {
    # We assume an awk implementation that is just plain dumb.
    # We will convert an character to its ASCII value with the
    # table ord[], and produce two-digit hexadecimal output
    # without the printf("%02X") feature.

    EOL = "%0A"     # "end of line" string (encoded)
    split ("1 2 3 4 5 6 7 8 9 A B C D E F", hextab, " ")
    hextab [0] = 0
    for ( i=1; i<=255; ++i ) ord [ sprintf ("%c", i) "" ] = i + 0
    if ("'"$EncodeEOL"'" == "yes") EncodeEOL = 1; else EncodeEOL = 0
    }
    {
    encoded = ""
    for ( i=1; i<=length ($0); ++i ) {
        c = substr ($0, i, 1)
        if ( c ~ /[a-zA-Z0-9.-]/ ) {
        encoded = encoded c     # safe character
        } else if ( c == " " ) {
        encoded = encoded "+"   # special handling
        } else {
        # unsafe character, encode it as a two-digit hex-number
        lo = ord [c] % 16
        hi = int (ord [c] / 16);
        encoded = encoded "%" hextab [hi] hextab [lo]
        }
    }
    if ( EncodeEOL ) {
        printf ("%s", encoded EOL)
    } else {
        print encoded
    }
    }
    END {
        #if ( EncodeEOL ) print ""
    }
' "$@"

มีรูปแบบง่าย ๆ ในการรับการเข้ารหัส UTF-8 แทนที่จะเป็น ASCII หรือไม่?
avgvstvs

15

นี่อาจเป็นสิ่งที่ดีที่สุด:

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

สิ่งนี้ใช้ได้กับฉันด้วยการเพิ่มสองครั้ง: 1. แทนที่ -e ด้วย -n เพื่อหลีกเลี่ยงการเพิ่มบรรทัดใหม่ในตอนท้ายของการโต้แย้งและ 2. เพิ่ม '%%' ไปยังสตริง printf เพื่อใส่% ด้านหน้าของแต่ละคู่ของ เลขฐานสิบหก
Rob Fagen

ทำงานหลังจากเพิ่มafter=$(echo -e ...
เครื่องหมาย

1
โปรดอธิบายวิธีการใช้งาน odคำสั่งไม่ธรรมดา
Mark Stosberg

นี้ไม่ได้ทำงานกับ OS X ของodเพราะใช้รูปแบบการออกที่แตกต่างกันกว่า odGNU ตัวอย่างเช่นprintf aa|od -An -tx1 -v|tr \ -พิมพ์-----------61--61--------------------------------------------------------พร้อม OS X ของodและ-61-61มี odGNU คุณสามารถใช้od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\nกับ OS X odหรือ GNU odก็ได้ xxd -p|sed 's/../%&/g'|tr -d \\nทำสิ่งเดียวกันแม้ว่าxxdจะไม่ได้อยู่ใน POSIX แต่odเป็น
nisetama

2
แม้ว่างานอาจนี้ก็หนีออกมาทุกตัวเดียว
ชาร์ลี

11

นี่คือโซลูชัน Bash ที่ไม่เรียกใช้โปรแกรมภายนอก:

uriencode() {
  s="${1//'%'/%25}"
  s="${s//' '/%20}"
  s="${s//'"'/%22}"
  s="${s//'#'/%23}"
  s="${s//'$'/%24}"
  s="${s//'&'/%26}"
  s="${s//'+'/%2B}"
  s="${s//','/%2C}"
  s="${s//'/'/%2F}"
  s="${s//':'/%3A}"
  s="${s//';'/%3B}"
  s="${s//'='/%3D}"
  s="${s//'?'/%3F}"
  s="${s//'@'/%40}"
  s="${s//'['/%5B}"
  s="${s//']'/%5D}"
  printf %s "$s"
}

4
สิ่งนี้จะทำงานแตกต่างกันระหว่างเวอร์ชันทุบตี บน RHEL 6.9 การทุบตีคือ 4.1.2 และรวมการเสนอราคาเดียว ในขณะที่เดเบียน 9 และทุบตี 4.4.12 ใช้ได้ดีกับเครื่องหมายคำพูดเดี่ยว สำหรับฉันการลบเครื่องหมายคำพูดเดียวทำให้มันใช้ได้ทั้งสองอย่าง s = "$ {s // ',' /% 2C}"
muni764

1
ฉันได้อัปเดตคำตอบเพื่อให้สอดคล้องกับสิ่งที่คุณค้นพบ @ muni764
davidchambers

แค่คำเตือน ... นี่จะไม่เข้ารหัสสิ่งต่าง ๆ เช่นตัวละครá
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 แม้ว่าคุณไม่จำเป็นต้องใส่มันลงใน var ถ้าคุณต้องการ BTW ไม่ได้รวม sed สำหรับแท็บคิดว่ามันจะเปลี่ยนเป็นช่องว่าง


5
ฉันรู้สึกว่านี่ไม่ใช่วิธีที่แนะนำให้ทำ
Cody Gray

2
อธิบายความรู้สึกของคุณได้โปรด .... เพราะฉันมีสิ่งที่ฉันได้กล่าวถึงผลงานและฉันได้ใช้มันในหลายสคริปต์เพื่อให้ฉันรู้ว่ามันทำงานได้สำหรับทุกตัวอักษรที่ฉันระบุไว้ ดังนั้นโปรดอธิบายว่าทำไมบางคนถึงไม่ใช้รหัสของฉันและใช้ Perl เพราะชื่อเรื่องนี้คือ "URLEncode จากสคริปต์ทุบตี" ไม่ใช่สคริปต์ Perl
manoflinux

บางครั้งไม่จำเป็นต้องใช้วิธีแก้ปัญหาไข่มุกดังนั้นจึงมีประโยชน์
Yuval Rimar

3
นี่ไม่ใช่วิธีที่แนะนำให้ทำเพราะบัญชีดำเป็นแนวทางปฏิบัติที่ไม่ดีและนี่เป็นยูนิโค้ดที่ไม่เป็นมิตรอยู่ดี
Ekevoo

นี่เป็นวิธีที่เป็นมิตรที่สุดที่เข้ากันได้กับ cat file.txt
mrwaim


7

สำหรับผู้ที่กำลังมองหาวิธีการแก้ปัญหาที่ไม่ต้องการ perl นี่คือสิ่งที่ต้องการเพียง hexdump และ awk:

url_encode() {
 [ $# -lt 1 ] && { return; }

 encodedurl="$1";

 # make sure hexdump exists, if not, just give back the url
 [ ! -x "/usr/bin/hexdump" ] && { return; }

 encodedurl=`
   echo $encodedurl | hexdump -v -e '1/1 "%02x\t"' -e '1/1 "%_c\n"' |
   LANG=C awk '
     $1 == "20"                    { printf("%s",   "+"); next } # space becomes plus
     $1 ~  /0[adAD]/               {                      next } # strip newlines
     $2 ~  /^[a-zA-Z0-9.*()\/-]$/  { printf("%s",   $2);  next } # pass through what we can
                                   { printf("%%%s", $1)        } # take hex value of everything else
   '`
}

เย็บติดกันจากสองแห่งทั่วทั้งเน็ตและการลองผิดลองถูก มันใช้งานได้ดี!


7

uni2asciiมีประโยชน์มาก:

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

2
นี้ไม่ได้ทำงานให้กับตัวละครภายในช่วง ASCII ที่จำเป็นต้องอ้างเช่น%และพื้นที่ (สุดท้ายที่สามารถแก้กับ-sธง)
Boldewyn

7

หากคุณไม่ต้องการพึ่ง Perl คุณสามารถใช้ sed มันค่อนข้างยุ่งเพราะตัวละครแต่ละตัวจะต้องหลบหนีที ทำไฟล์ที่มีเนื้อหาต่อไปนี้และเรียกมันurlencode.sed

s/%/%25/g
s/ /%20/g
s/ /%09/g
s/!/%21/g
s/"/%22/g
s/#/%23/g
s/\$/%24/g
s/\&/%26/g
s/'\''/%27/g
s/(/%28/g
s/)/%29/g
s/\*/%2a/g
s/+/%2b/g
s/,/%2c/g
s/-/%2d/g
s/\./%2e/g
s/\//%2f/g
s/:/%3a/g
s/;/%3b/g
s//%3e/g
s/?/%3f/g
s/@/%40/g
s/\[/%5b/g
s/\\/%5c/g
s/\]/%5d/g
s/\^/%5e/g
s/_/%5f/g
s/`/%60/g
s/{/%7b/g
s/|/%7c/g
s/}/%7d/g
s/~/%7e/g
s/      /%09/g

หากต้องการใช้งานให้ทำดังต่อไปนี้

STR1=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f1)
STR2=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f2)
OUT2=$(echo "$STR2" | sed -f urlencode.sed)
echo "$STR1?$OUT2"

สิ่งนี้จะแบ่งสตริงออกเป็นส่วนที่ต้องการการเข้ารหัสและส่วนที่ใช้ได้การเข้ารหัสส่วนที่ต้องการแล้วเย็บกลับเข้าด้วยกัน

คุณสามารถใส่มันลงในสคริปต์ sh เพื่อความสะดวกอาจจะต้องใช้พารามิเตอร์ในการเข้ารหัสวางไว้บนเส้นทางของคุณแล้วคุณสามารถโทรไปที่:

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

แหล่ง


7

คุณสามารถเลียนแบบจาวาสคริปต์encodeURIComponentใน perl นี่คือคำสั่ง:

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

คุณสามารถตั้งค่านี้เป็นนามแฝงทุบตีใน.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
การหยุดพักนี้จะไม่เกิดขึ้นหากมีอักขระอื่น ๆ ในสตริงที่ไม่ถูกต้องระหว่างเครื่องหมายคำพูดเดี่ยวเช่นเครื่องหมายแบ็กสแลชเดียวหรือบรรทัดใหม่
Stuart P. Bentley

จุดดี. ถ้าเราจะไปที่ปัญหาของการหลบหนีตัวละครที่มีปัญหาทั้งหมดใน Bash เราก็อาจจะทำหน้าที่แทนโดยตรงและหลีกเลี่ยงnodeทั้งหมด ฉันโพสต์โซลูชัน Bash-only :)
davidchambers

1
ตัวแปรนี้พบที่อื่นในหน้าเพื่อหลีกเลี่ยงปัญหาการอ้างอิงโดยการอ่านค่าจาก STDIN:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg

6

คำถามคือเกี่ยวกับการทำเช่นนี้ใน bash และไม่จำเป็นต้องใช้ python หรือ Perl เพราะมีคำสั่งเดียวที่ทำสิ่งที่คุณต้องการ - "urlencode"

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

นี่เป็นสิ่งที่ดีกว่าเช่นกันตัวอย่างข้างต้นไม่ได้เข้ารหัสอักขระทั้งหมดอย่างถูกต้อง ลองด้วยเส้นประยาวที่คุณได้รับจาก Word และคุณได้รับการเข้ารหัสผิด

หมายเหตุคุณต้องติดตั้ง "gridsite-clients" เพื่อให้คำสั่งนี้


1
รุ่นของฉันทุบตี (GNU 3.2) urlencodeไม่ได้มี คุณใช้เวอร์ชั่นอะไร
Sridhar Sarnobat

1
ฉันมี 4.3.42 แต่คำสั่ง urlencode นั้นจัดทำโดย "gridsite-clients" ลองติดตั้งและคุณควรจะปรับ
Dylan

5
ดังนั้นคำตอบของคุณจึงไม่ดีไปกว่าสิ่งที่ต้องการให้ผู้อื่นติดตั้ง (python, perl, lua, …)
Cyrille Pontvieux

ยกเว้นว่ามันจะต้องติดตั้งยูทิลิตี้เดียวแทนทั้งภาษา (และห้องสมุด) รวมทั้งง่ายและชัดเจนสุด ๆ เพื่อดูว่ามันกำลังทำอะไรอยู่
Dylan

ลิงค์แรกสำหรับหน้าแพ็คเกจ / โครงการที่ให้คำสั่งนี้จะเป็นประโยชน์
Doron Behar



4

อีกวิธี PHP:

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

2
echoจะต่อท้ายอักขระบรรทัดใหม่ (ฐานสิบหก0xa) echo -nที่จะหยุดมันทำที่ใช้
Mathew Hall

3

นี่คือรุ่นของฉันสำหรับ busybox ash shell สำหรับระบบสมองกลฝังตัวฉันเริ่มใช้ตัวแปรของ Orwellophile:

urlencode()
{
    local S="${1}"
    local encoded=""
    local ch
    local o
    for i in $(seq 0 $((${#S} - 1)) )
    do
        ch=${S:$i:1}
        case "${ch}" in
            [-_.~a-zA-Z0-9]) 
                o="${ch}"
                ;;
            *) 
                o=$(printf '%%%02x' "'$ch")                
                ;;
        esac
        encoded="${encoded}${o}"
    done
    echo ${encoded}
}

urldecode() 
{
    # urldecode <string>
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}

2

นี่คือฟังก์ชั่น POSIX ที่จะทำ:

encodeURIComponent() {
  awk 'BEGIN {while (y++ < 125) z[sprintf("%c", y)] = y
  while (y = substr(ARGV[1], ++j, 1))
  q = y ~ /[[:alnum:]_.!~*\47()-]/ ? q y : q sprintf("%%%02X", z[y])
  print q}' "$1"
}

ตัวอย่าง:

value=$(encodeURIComponent "$2")

แหล่ง


2

นี่คือการแปลงแบบบรรทัดเดียวโดยใช้ Lua คล้ายกับคำตอบของ Blueyedยกเว้นRFC 3986 อักขระที่ไม่ได้จองทุกตัวไม่มีการเข้ารหัส (เหมือนคำตอบนี้ ):

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

นอกจากนี้คุณอาจต้องตรวจสอบให้แน่ใจว่าบรรทัดใหม่ในสตริงของคุณถูกแปลงจาก LF เป็น CRLF ซึ่งในกรณีนี้คุณสามารถแทรก a 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

นี่คือคำตอบของ orwellophile เวอร์ชัน ksh ที่มีฟังก์ชั่น rawurlencode และ rawurldecode (ลิงค์: วิธีการ urlencode data สำหรับคำสั่ง 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 ที่ดีกว่าจาวาสคริปต์?

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

เกินขอบเขตคำถาม ไม่ทุบตีไม่ม้วนงอ แม้ว่าฉันจะแน่ใจว่าทำงานได้ดีมากถ้ามีโหนด
Cyrille Pontvieux

ทำไมการลงคะแนนเสียงแบบนี้และไม่ใช่คำตอบของ python / perl นอกจากนี้วิธีนี้จะไม่ตอบคำถามเดิม "วิธี urlencode ข้อมูลสำหรับคำสั่ง curl" สิ่งนี้สามารถใช้งานได้จากสคริปต์ทุบตีและสามารถให้ผลลัพธ์กับคำสั่ง curl
Nestor Urquiza

ฉันลงคะแนนให้คนอื่นด้วย คำถามคือทำอย่างไรในสคริปต์ทุบตี หากใช้ภาษาอื่นเช่น node / js, python หรือ perl ไม่จำเป็นต้องใช้ curl โดยตรง
Cyrille Pontvieux

2
ในขณะที่ฉันไม่ต้องกังวลกับการลงคะแนนปัญหาของคำสั่งนี้ก็คือมันต้องมีข้อมูลที่จะหลบหนีอย่างถูกต้องเพื่อใช้ในจาวาสคริปต์ เช่นลองกับคำพูดเดียวและความบ้าคลั่งกลับทับหลังบางอย่าง ถ้าคุณต้องการใช้โหนดคุณควรอ่านสิ่งต่าง ๆ จาก stdin เช่นnode -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin - แฮ็กเกอร์

1
ระวังด้วยวิธีแก้ปัญหาของ @ MichaelKrelin-hacker หากคุณกำลังไพพ์ข้อมูลจาก STDIN ตรวจสอบให้แน่ใจว่าไม่รวมบรรทัดใหม่ที่ต่อท้าย ตัวอย่างเช่นecho | ...เกิดข้อผิดพลาดขณะที่echo -n | ...หยุดการขึ้นบรรทัดใหม่
Mark Stosberg

0

ต่อไปนี้เป็นไปตามคำตอบของ Orwellophile แต่แก้ข้อบกพร่องหลายไบต์ที่กล่าวถึงในความคิดเห็นโดยการตั้งค่า 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}"
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.