我试图写一个bash脚本测试,需要一个参数,并通过curl发送到网站。我需要url编码的值,以确保特殊字符被正确处理。最好的方法是什么?
这是我到目前为止的基本脚本:
#!/bin/bash
host=${1:?'bad host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@
我试图写一个bash脚本测试,需要一个参数,并通过curl发送到网站。我需要url编码的值,以确保特殊字符被正确处理。最好的方法是什么?
这是我到目前为止的基本脚本:
#!/bin/bash
host=${1:?'bad host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@
当前回答
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中。顺便说一句,没有包括sed for选项卡,认为它会把它变成空格
其他回答
如果不想依赖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
源
我发现下面的代码片段很有用,可以将它插入到一个程序调用链中,其中URI::Escape可能没有安装:
perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg'
(源)
This is a simpler pure bash/ksh version without the substring logic. Stated differently the other pure shell solutions reparsed the string to get each character (using parameter substitution ${#str} for the lenght and ${str:$i:1} to discover each character). The below method does just one loop over the string to process each character. It is the difference between O(n^2) and O(n). In this answer: https://stackoverflow.com/a/40833433/1344599 Thunderbeef saw ~150x speed improvement on a large text file. This solution is also a shorter oneliner:
while IFS='' read -n 1 c ; do [[ "$c" =~ [A-Za-z0-9.~_-] ]] && printf "$c" || printf '%%%02X' "'$c" ; done
在函数中,你可以使用stdin或形参:
function urlen_stdin {
while IFS='' read -n 1 c ; do [[ "$c" =~ [A-Za-z0-9.~_-] ]] && printf "$c" || printf '%%%02X' "'$c" ; done
}
function urlen_param {
printf '%s' "$1" | while IFS='' read -n 1 c ; do [[ "$c" =~ [A-Za-z0-9.~_-] ]] && printf "$c" || printf '%%%02X' "'$c" ; done
}
function urlen_here {
while IFS='' read -n 1 c ; do [[ "$c" =~ [A-Za-z0-9.~_-] ]] && printf "$c" || printf '%%%02X' "'$c" ; done <<< "$1"
}
#usage:
echo -n 'hello !@#$%^&*()[]:;{}\/|-_=+.,? world' | urlen_stdin
urlen_param 'hello !@#$%^&*()[]:;{}\/|-_=+.,? world'
urlen_here 'hello !@#$%^&*()[]:;{}\/|-_=+.,? world'
# all methods render:
hello%20%21%40%23%24%25%5E%26%2A%28%29%5B%5D%3A%3B%7B%7D%2F%7C-_%3D%2B.%2C%3F%20world
解释:
IFS= "使空格像普通字符一样 Read -n 1一次读取1个字符 [[=~]]是一个正则表达式比较。如果字符匹配,则遵循&&路径,否则遵循||路径 printf '%%%02X'打印一个%和字符作为零填充长度为2的十六进制代码
Orwellophile给出了一个很好的答案,它包含了一个纯bash选项(函数rawurlencode),我在我的网站上使用过(基于shell的CGI脚本,响应搜索请求的大量url)。唯一的缺点是高峰期间CPU过高。
我找到了一个改进的解决方案,利用bash的“全局替换”特性。有了这个解决方案,url编码的处理时间快了4倍。解决方案确定要转义的字符,并使用“全局替换”操作符(${var//source/replacement})来处理所有替换。这种速度的提高显然来自于使用bash内部循环,而不是显式循环。
性能:核心i3-8100 3.60Ghz。测试用例:来自堆栈溢出的1000个URL,类似于这个票据:“https://stackoverflow.com/questions/296536/how-to-urlencode-data-for-curl-command”。
现有解决方案:0.807秒 优化方案:0.162秒(5倍加速)
url_encode()
{
local key="${1}" varname="${2:-_rval}" prefix="${3:-_ENCKEY_}"
local unsafe=${key//[-_.~a-zA-Z0-9 ]/}
local -i key_len=${#unsafe}
local ch ch1 ch0
while [ "$unsafe" ] ;do
ch=${unsafe:0:1}
ch0="\\$ch"
printf -v ch1 '%%%02x' "'$ch'"
key=${key//$ch0/"$ch1"}
unsafe=${unsafe//"$ch0"}
done
key=${key// /+}
REPLY="$key"
# printf "%s" "$REPLY"
return 0
}
作为一个次要的额外字符,它使用'+'来编码空格。稍微紧凑的URL。
基准:
function t {
local key
for (( i=1 ; i<=$1 ; i++ )) do url_encode "$2" kkk2 ; done
echo "K=$REPLY"
}
t 1000 "https://stackoverflow.com/questions/296536/how-to-urlencode-data-for-curl-command"
在本例中,我需要对主机名进行URL编码。不要问为什么。作为一个极简主义者和Perl爱好者,下面是我想到的。
url_encode()
{
echo -n "$1" | perl -pe 's/[^a-zA-Z0-9\/_.~-]/sprintf "%%%02x", ord($&)/ge'
}
很适合我。