如何为curl命令urlencode数据?

我正在尝试编写一个bash脚本进行testing,它需要一个参数并通过curl将其发送到网站。 我需要URL的编码值,以确保特殊字符处理正确。 什么是最好的方法来做到这一点?

到目前为止,这是我的基本脚本:

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

使用curl --data-urlencode ; 从man curl

这发布的数据,类似于其他 – --data选项,但执行URL编码的例外。 为了符合CGI, <data>部分应该以一个名字开头,后跟一个分隔符和一个内容规范。

用法示例:

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

请参阅手册页获取更多信息。

这需要curl7.18.0或更新(2008年1月发布) 。 用curl -V来检查你的版本。

这是纯粹的BASH答案。

 rawurlencode() { local string="${1}" local strlen=${#string} local encoded="" local pos co for (( pos=0 ; pos<strlen ; pos++ )); do c=${string:$pos:1} case "$c" in [-_.~a-zA-Z0-9] ) o="${c}" ;; * ) printf -vo '%%%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 } 

使用匹配的集合,我们现在可以执行一些简单的testing:

 $ 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 

在你的bash脚本的第二行使用Perl的URI::Escape模块和uri_escape函数:

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

编辑:修正引用的问题,由Chris Johnsen在评论中build议。 谢谢!

为了完整起见,许多使用sedawk解决scheme只能转换一组特殊的字符,因此代码量相当大,也不会翻译其他需要编码的特殊字符。

一个安全的方式urlencode将只是编码每一个单一的字节 – 即使是那些将被允许。

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

xxd在这里注意input是作为字节而不是字符来处理的。

编辑:

xxd附带了Debian中的vim-common软件包,我只是在没有安装它的系统上,我不想安装它。 替代scheme是使用Debian中的bsdmainutils包中的hexdump 。 根据下面的图表,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' 

我发现它在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 

我发现下面的代码片段很有用,可以将其粘贴到一个程序调用链中,其中URI :: Escape可能不会被安装:

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

( 来源 )

其中一个变种,可能是丑陋的,但很简单:

 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 } 

如果你想运行GET请求并使用纯curl,只需添加--get to @ Jacob的解决scheme。

这里是一个例子:

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

直接链接到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 ie 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 ABCDEF", 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 "" } ' "$@" 

另一个select是使用jq

 jq -s -R -r @uri 

-s (– --slurp )将input行读入数组, -s -R (– --slurp --raw-input )将input读入单个string。 -r (– --raw-output )输出string的内容而不是JSONstring文字。

或者这个百分比编码所有字节:

 xxd -p|tr -d \\n|sed 's/../%&/g' 
 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内的string,并输出到$ url。 虽然你不需要把它放在一个变种,如果你想。 顺便说一句,没有包括标签认为它会把它变成空间

对于那些寻找不需要perl的解决scheme的人来说,这里只需要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 '` } 

从网上的几个地方缝合在一起,一些本地的试错。 这很好用!

这可能是最好的一个:

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

从shell脚本使用php:

 value="http://www.google.com" encoded=$(php -r "echo rawurlencode('$value');") # encoded = "http%3A%2F%2Fwww.google.com" echo $(php -r "echo rawurldecode('$encoded');") # returns: "http://www.google.com" 
  1. http://www.php.net/manual/en/function.rawurlencode.php
  2. http://www.php.net/manual/en/function.rawurldecode.php

uni2ascii非常方便:

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

如果你不想依靠Perl,你也可以使用sed。 这有点乱,因为每个angular色都必须单独逃脱。 用以下内容创build一个文件,并将其称为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" 

这将把string拆分成需要编码的部分,并且部分很好,对需要它的部分进行编码,然后拼接在一起。

你可以把它放到一个sh脚本中,方便起见,也许它需要一个参数来编码,把它放在你的path上,然后你可以调用:

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

资源

你可以在perl中模拟javascript的encodeURIComponent 。 这是命令:

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

您可以将其设置为.bash_profile的bash别名:

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

现在你可以pipe入encodeURIComponent

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

这是节点版本:

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

简单的PHP选项:

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

Ruby,为了完整性

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

另一个PHP方法:

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

问题是关于在bash中这样做,并且不需要python或perl,因为实际上有一个命令完全按照你想要的来执行 – “urlencode”。

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

这也好多了,因为上面的perl答案,例如,不正确地编码所有字符。 尝试一下从Word获得的长划线,并得到错误的编码。

请注意,您需要安装“gridsite-clients”才能提供此命令。

这是一个POSIX函数来做到这一点:

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

例:

 value=$(encodeURIComponent "$2") 

资源

这是一个使用Lua的单行转换,类似于blueyed的答案,除了所有的RFC 3986 Unreserved Characters留下未编码(就像这样的答案 ):

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

此外,您可能需要确保将string中的换行符从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") 

这是一个Bash解决scheme,它不会调用任何外部程序:

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

安装PHP我使用这种方式:

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

这是包含rawurlencode和rawurldecode函数(链接: 如何为curl命令urlencode数据? )的orshophile答案的ksh版本。 我没有足够的代表发表评论,因此新的职位..

 #!/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++ 

下面是我为busybox ash shell的一个embedded式系统版本,我最初采用了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}" } 

什么会parsingURL比JavaScript更好?

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