我正在尝试编写一个用于测试的bash脚本,它接受一个参数并通过curl将其发送到网站.我需要对值进行url编码,以确保正确处理特殊字符.做这个的最好方式是什么?
这是我到目前为止的基本脚本:
#!/bin/bash host=${1:?'bad host'} value=$2 shift shift curl -v -d "param=${value}" http://${host}/somepath $@
Jacob Rask.. 359
使用curl --data-urlencode
; 来自man curl
:
这会发布数据,类似于其他
--data
选项,但执行URL编码除外.要符合CGI,该部件应以名称开头,后跟分隔符和内容规范.
用法示例:
curl \ --data-urlencode "paramName=value" \ --data-urlencode "secondParam=value" \ http://example.com
有关详细信息,请参见手册页.
这需要卷曲7.18.0或更新版本(2008年1月发布).使用 curl -V
来检查你所拥有的版本.
使用curl --data-urlencode
; 来自man curl
:
这会发布数据,类似于其他
--data
选项,但执行URL编码除外.要符合CGI,该部件应以名称开头,后跟分隔符和内容规范.
用法示例:
curl \ --data-urlencode "paramName=value" \ --data-urlencode "secondParam=value" \ http://example.com
有关详细信息,请参见手册页.
这需要卷曲7.18.0或更新版本(2008年1月发布).使用 curl -V
来检查你所拥有的版本.
这是纯粹的BASH答案.
rawurlencode() { local string="${1}" local strlen=${#string} local encoded="" local pos c o for (( pos=0 ; pos您可以通过两种方式使用它:
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
不幸的是,这个脚本在某些字符上失败,例如'é'和'½',分别输出'e%FFFFFFFFFFFFFFCC'和'%FFFFFFFFFFFFFFC2'(我相信每个字符循环的b/c).
3> dubek..:在bash脚本的第二行使用Perl的
URI::Escape
模块和uri_escape
函数:... value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")" ...编辑:修复引用问题,如Chris Johnsen在评论中所建议的那样.谢谢!
你也不用`echo`:`value ="$(perl -MURI :: Escape -e'print uri_escape($ ARGV [0]);'"$ 2")"`
如果$ 2包含撇号,则无效.
可能没有安装URI :: Escape,在这种情况下请检查我的答案.
4> josch..:为了完整起见,许多解决方案使用
sed
或awk
仅翻译一组特殊字符,因此代码大小非常大,也不会翻译应编码的其他特殊字符.urlencode的一种安全方法是对每个字节进行编码 - 即使是那些已被允许的字节.
echo -ne 'some random\nbytes' | xxd -plain | tr -d '\n' | sed 's/\(..\)/%\1/g'xxd在这里注意输入是作为字节而不是字符处理的.
编辑:
xxd附带了Debian中的vim-common软件包,我只是在没有安装它的系统上,我不想安装它.altornative是使用
hexdump
Debian中的bsdmainutils 包.根据下图,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'
做得很好 - 很高兴看到只使用shell的单线程.
@qdii确实-n缺少echo,但`xxd`调用属于`tr -d`调用.它属于那里,所以`foobar`中的任何换行都由`xxd`翻译.`xxd`调用后的`tr -d`是删除xxd产生的换行符.似乎你从来没有足够长的foobar,所以`xxd`产生换行,但是对于长输入它会.所以`tr -d`是必要的.与你的假设相反,`tr -d`不是要从输入中删除换行符,而是从`xxd`输出中删除换行符.我想在输入中保留换行符.唯一有效的一点是,echo添加了一个不必要的换行符.
如果我听起来像这样,我不是故意粗鲁.这是一个非常好的脚本,我实际上使用它:)
@qdii为什么?这不仅会使urlencode新行不可能,而且还会错误地将xxd创建的换行符插入到输出中.
@josch我不知道xxd那样做了,谢谢告诉我并修复小bug :)
5> 小智..:其中一个变种,可能很难看,但很简单:
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/'
这绝对是辉煌的!我真的希望你把它留下一行,以便人们可以看到它真的很简单.要对`date`命令的结果进行URL编码...`date | curl -Gso/dev/null -w%{url_effective} --data-urlencode @ - ""| cut -c 3-`(你必须'剪掉'前两个字符,因为curl的输出在技术上是一个带有查询字符串的相对URL.)
要避免在结尾处使用'%0A`,请使用`printf`而不是`echo`.
@BrunoBronosky您的单行变体很好,但似乎在编码结束时添加了"%0A".用户要小心.功能版本似乎没有此问题.
一个班轮很棒
6> 小智..:我发现它在python中更具可读性:
encoded_value=$(python -c "import urllib; print urllib.quote('''$value''')")三重'确保单值报价不会受到伤害.urllib在标准库中.它适用于这个疯狂(真实世界)网址的例子:
"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7
我在引用和使用三重引号的特殊字符时遇到了一些麻烦,这似乎基本上适用于所有事情:encoded_value ="$(echo -n"$ {data}"| python -c"import urllib; import sys; sys.stdout. write(urllib.quote(sys.stdin.read()))")";
引用`sys.argv`而不是将`$ value`替换为后来被解析为代码的字符串会更安全.如果`value`包含`'''+ __import __("os").system("rm -rf~")+'''`
python -c“ import urllib; print urllib.quote(raw_input())” <<<“ $ data”`
7> nisetama..:另一种选择是使用
jq
:jq -sRr @uri
-R
(--raw-input
)将输入行视为字符串,而不是将它们解析为JSON,并且-sR
(--slurp --raw-input
)将输入读入单个字符串.-r
(--raw-output
)输出字符串的内容而不是JSON字符串文字.如果输入不包含换行符(或者您不想将它们转义为
jq
),则可以不使用-n
该--null-input
选项.或者这个百分比编码所有字节:
jq -nr --arg v "my shell string" '$v|@uri'
对于任何想和我一样的人:@uri不是变量,而是用于格式化字符串和转义的文字jq过滤器;有关详细信息,请参见[jq手册](https://stedolan.github.io/jq/manual)(对不起,没有直接链接,需要在页面上搜索`@ uri`)。
8> blueyed..:我发现以下代码片段可用于将其粘贴到程序调用链中,其中可能未安装URI :: Escape:
perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg'(来源)
为我工作.我将它改为perl -lpe ...(字母椭圆).这删除了我的目的所需的尾随换行符.
具体取决于您需要编码的字符,您可以将其简化为"perl -pe"/(\ W)/ sprintf("%%% 02X",ord($ 1))/ ge'`,它允许使用字母,数字和下划线,但编码其他一切.
9> Piotr Czapla..:如果你想运行
GET
请求并使用纯卷曲,只需添加--get
到@ Jacob的解决方案.这是一个例子:
curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
10> chenzhiwei..:这可能是最好的一个:
after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
11> MatthieuP..:直接链接到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 "" } ' "$@"
12> manoflinux..: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 for tab认为它会把它变成空格
我觉得这不是*推荐的方法.
这不是推荐的方法,因为黑名单是不好的做法,无论如何这是unicode不友好.
请解释一下你的感受....因为我说的是我所说的作品而且我已经在几个剧本中使用它,所以我知道它适用于我列出的所有字符.所以请解释为什么有人不会使用我的代码并使用perl,因为标题是"来自bash脚本的URLEncode"而不是perl脚本.
13> davidchamber..:这是一个不调用任何外部程序的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" }
这在bash版本之间表现不同.在RHEL 6.9上,bash是4.1.2,它包含单引号.虽然Debian 9和bash 4.4.12对单引号很好.对我来说,删除单引号使它适用于两者.S = "$ {S // ''/%2C}"
14> Louis Marasc..:对于那些寻找不需要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 '` }从网上的几个地方缝合在一起并进行一些本地试验和错误.它很棒!
15> Darren Weber..:在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"
http://www.php.net/manual/en/function.rawurlencode.php
http://www.php.net/manual/en/function.rawurldecode.php
16> Jay..:如果您不想依赖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资源
17> kev..:uni2ascii非常方便:
$ echo -ne '????' | uni2ascii -aJ %E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
这不适用于ASCII范围内的字符*,需要引用,如`%`和空格(最后可以用`-s`标志来补救)
18> Klaus..:你可以
encodeURIComponent
在perl中模拟javascript .这是命令:perl -pe 's/([^a-zA-Z0-9_.!~*()'\''-])/sprintf("%%%02X", ord($1))/ge'您可以将其设置为bash别名
.bash_profile
:alias encodeURIComponent='perl -pe '\''s/([^a-zA-Z0-9_.!~*()'\''\'\'''\''-])/sprintf("%%%02X",ord($1))/ge'\'现在你可以管道
encodeURIComponent
:$ echo -n 'hèllo wôrld!' | encodeURIComponent h%C3%A8llo%20w%C3%B4rld!
19> davidchamber..:这是节点版本:
uriencode() { node -p "encodeURIComponent('${1//\'/\\\'}')" }
20> 小智..:问题是在bash中执行此操作并且不需要python或perl,因为事实上只有一个命令可以完全按照您的要求运行 - "urlencode".
value=$(urlencode "${2}")这也好得多,因为例如上面的perl答案没有正确编码所有字符.尝试使用从Word获得的长划线,你得到错误的编码.
注意,您需要安装"gridsite-clients"来提供此命令.
所以你的答案并不比任何需要安装别人的东西好(python,perl,lua,...)
21> Ryan..:简单的PHP选项:
echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'