我试图写一个bash脚本测试,需要一个参数,并通过curl发送到网站。我需要url编码的值,以确保特殊字符被正确处理。最好的方法是什么?

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

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

当前回答

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"

其他回答

我发现下面的代码片段很有用,可以将它插入到一个程序调用链中,其中URI::Escape可能没有安装:

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

(源)

简单的PHP选项:

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

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

...

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

编辑:修复引用问题,正如Chris Johnsen在评论中建议的那样。谢谢!

对于我的一个案例,我发现NodeJS url库有最简单的解决方案。当然是YMMV

$ urlencode(){ node -e "console.log(require('url').parse(process.argv.slice(1).join('+')).href)" "$@"; }

$ urlencode "https://example.com?my_database_has=these 'nasty' query strings in it"
https://example.com/?my_database_has=these%20%27nasty%27%20query%20strings%20in%20it

安装php后,我使用这种方式:

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