我试图写一个bash脚本测试,需要一个参数,并通过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

有关更多信息,请参阅手册页。

这需要curl 7.18.0或更新版本(发布于2008年1月)。使用curl -V来检查您拥有的版本。

你也可以对查询字符串进行编码:

curl --get \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202

其他回答

如果你想运行GET请求并使用纯curl,只需添加—得到@Jacob的解决方案。

这里有一个例子:

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

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'

(源)

Note

这些函数不是用来编码URL的数据,而是用来编码URL。 将url以每行一个的方式放入文件中。

#!/bin/dash

replaceUnicodes () { # $1=input/output file
    if ! mv -f "$1" "$1".tmp 2>/dev/null; then return 1; fi
    output="$1" awk '
    function hexValue(chr) {
        if(chr=="0") return 0; if(chr=="1") return 1; if(chr=="2") return 2; if(chr=="3") return 3; if(chr=="4") return 4; if(chr=="5") return 5;
        if(chr=="6") return 6; if(chr=="7") return 7; if(chr=="8") return 8; if(chr=="9") return 9; if(chr=="A") return 10;
        if(chr=="B") return 11; if(chr=="C") return 12; if(chr=="D") return 13; if(chr=="E") return 14; return 15 }
    function hexToDecimal(str,  value,i,inc) {
        str=toupper(str); value=and(hexValue(substr(str,length(str),1)),15); inc=1;
        for(i=length(str)-1;i>0;i--) {
            value+=lshift(hexValue(substr(str,i,1)),4*inc++)
        } return value }
    function toDecimal(str, value,i) {
        for(i=1;i<=length(str);i++) {
            value=(value*10)+substr(str,i,1)
        } return value }
    function to32BE(high,low) {
        # return 0x10000+((high-0xD800)*0x400)+(low-0xDC00) }
        return lshift((high-0xD800),10)+(low-0xDC00)+0x10000 }
    function toUTF8(value) {
        if(value<0x80) { 
            return sprintf("%%%02X",value)
        } else if(value>0xFFFF) {
            return sprintf("%%%02X%%%02X%%%02X%%%02X",or(0xF0,and(rshift(value,18),0x07)),or(0x80,and(rshift(value,12),0x3F)),or(0x80,and(rshift(value,6),0x3F)),or(0x80,and(rshift(value,0),0x3F)))
        } else if(value>0x07FF) {
            return sprintf("%%%02X%%%02X%%%02X",or(0xE0,and(rshift(value,12),0x0F)),or(0x80,and(rshift(value,6),0x3F)),or(0x80,and(rshift(value,0),0x3F)))
        } else { return sprintf("%%%02X%%%02X",or(0xC0,and(rshift(value,6),0x1F)),or(0x80,and(rshift(value,0),0x3F))) }
    }
    function trap(str) { sub(/^\\+/,"\\",str); return str }
    function esc(str) { gsub(/\\/,"\\\\",str); return str }
    BEGIN { output=ENVIRON["output"] }
    {
        finalStr=""; while(match($0,/[\\]+u[0-9a-fA-F]{4}/)) {
            p=substr($0,RSTART,RLENGTH); num=hexToDecimal(substr(p,RLENGTH-3,4));
            bfrStr=substr($0,1,RSTART-1); $0=substr($0,RSTART+RLENGTH,length($0)-(RSTART+RLENGTH-1));
            if(surrogate) {
                surrogate=0;
                if(RSTART!=1 || num<0xD800 || (num>0xDBFF && num<0xDC00) || num>0xDFFF) {
                    finalStr=sprintf("%s%s%s%s",finalStr,trap(highP),bfrStr,toUTF8(num))
                } else if(num>0xD7FF && num<0xDC00) {
                    surrogate=1; high=num; finalStr=sprintf("%s%s",finalStr,trap(highP))
                } else { finalStr=sprintf("%s%s",finalStr,toUTF8(to32BE(high,num))) }
            } else if(num>0xD7FF && num<0xDC00) {
                surrogate=1; highP=p; high=num; finalStr=sprintf("%s%s",finalStr,bfrStr)
            } else { finalStr=sprintf("%s%s%s",finalStr,bfrStr,toUTF8(num)) }
        } finalStr=sprintf("%s%s",finalStr,$0); $0=finalStr

        while(match($0,/[\\]+U[0-9a-fA-F]{8}/)) {
            str=substr($0,RSTART,RLENGTH); gsub(esc(str),toUTF8(hexToDecimal(substr(str,RLENGTH-7,8))),$0)
        }
        while(match($0,/[\\]*&#[xX][0-9a-fA-F]{1,8};/)) {
            str=substr($0,RSTART,RLENGTH); idx=index(str,"#");
            gsub(esc(str),toUTF8(hexToDecimal(substr(str,idx+2,RLENGTH-idx-2))),$0)
        }
        while(match($0,/[\\]*&#[0-9]{1,10};/)) {
            str=substr($0,RSTART,RLENGTH); idx=index(str,"#");
            gsub(esc(str),toUTF8(toDecimal(substr(str,idx+1,RLENGTH-idx-1))),$0)
        }
        printf("%s\n",$0) > output
    }' "$1".tmp
    rm -f "$1".tmp
}

replaceHtmlEntities () { # $1=input/output file
    if ! mv -f "$1" "$1".tmp 2>/dev/null; then return 1; fi
    sed 's/%3[aA]/:/g; s/%2[fF]/\//g; s/&quot;/%22/g; s/&lt;/%3C/g; s/&gt;/%3E/g; s/&nbsp;/%A0/g; s/&cent;/%A2/g; s/&pound;/%A3/g; s/&yen;/%A5/g; s/&copy;/%A9/g; s/&reg;/%AE/g; s/&amp;/\&/g; s/\\*\//\//g' "$1".tmp > "$1"
    rm -f "$1".tmp
}


# "od -v -A n -t u1 -w99999999"
# "hexdump -v -e \47/1 \42%d \42\47"
# Reminder :: Do not encode (, ), [, and ].
toUTF8Encoded () { # $1=input/output file
    if ! mv -f "$1" "$1".tmp 2>/dev/null; then return 1; fi
    if [ -s "$1".tmp ]; then
        # od -A n -t u1 -w99999999 "$1".tmp | \
        hexdump -v -e '/1 "%d "' "$1".tmp | \
        output="$1" awk 'function hexDigit(chr) { if((chr>47 && chr<58) || (chr>64 && chr<71) || (chr>96 && chr<103)) return 1; return 0 }
        BEGIN { output=ENVIRON["output"] }
        {   for(i=1;i<=NF;i++) {
                flushed=0; c=$(i);
                if(c==13) { if($(i+1)==10) i++; printf("%s\n",url) > output; url=""; flushed=1
                } else if(c==10) { printf("%s\n",url) > output; url=""; flushed=1
                } else if(c==37) {
                    if(hexDigit($(i+1)) && hexDigit($(i+2))) {
                        url=sprintf("%s%%%c%c",url,$(i+1),$(i+2)); i+=2
                    } else { url=sprintf("%s%%25",url) }
                } else if(c>32 && c<127 && c!=34 && c!=39 && c!=96 && c!=60 && c!=62) {
                    url=sprintf("%s%c",url,c)
                } else { url=sprintf("%s%%%02X",url,c) }
            } if(!flushed) printf("%s\n",url) > output
        }'
    fi
    rm -f "$1".tmp
}

调用replacecodes () -> replacemlentities () --> toUTF8Encoded()

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

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