【问题标题】:How to urlencode data for curl command?如何对 curl 命令的数据进行 urlencode?
【发布时间】:2010-09-22 17:32:21
【问题描述】:

我正在尝试编写一个用于测试的 bash 脚本,该脚本接受一个参数并通过 curl 将其发送到网站。我需要对值进行 url 编码以确保正确处理特殊字符。做这个的最好方式是什么?

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

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

【问题讨论】:

标签: bash shell curl scripting urlencode


【解决方案1】:

这是 orwellophile 答案的 ksh 版本,其中包含 rawurlencode 和 rawurldecode 函数(链接:How to urlencode data for curl command?)。我没有足够的代表发表评论,因此新帖子..

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

【讨论】:

    【解决方案2】:

    以下是基于Orwellophile的回答,但解决了多字节 通过设置 LC_ALL=C(来自 vte.sh 的技巧)在 cmets 中提到的错误。 我已经把它写成函数适合 PROMPT_COMMAND 的形式,因为 我就是这么用的。

    print_path_url() {
      local LC_ALL=C
      local string="$PWD"
      local strlen=${#string}
      local encoded=""
      local pos c o
    
      for (( pos=0 ; pos<strlen ; pos++ )); do
         c=${string:$pos:1}
         case "$c" in
            [-_.~a-zA-Z0-9/] ) o="${c}" ;;
            * )               printf -v o '%%%02x' "'$c"
         esac
         encoded+="${o}"
      done
      printf "\033]7;file://%s%s\007" "${HOSTNAME:-}" "${encoded}"
    }
    

    【讨论】:

      【解决方案3】:

      对于我的一个案例,我发现 NodeJS url lib 有最简单的解决方案。当然是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
      

      【讨论】:

      • 为什么投反对票?该解决方案可能效率低下,但绝对正确,并且不像其他人那样手工制作......
      【解决方案4】:

      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
      }
      
      

      作为一个小额外,它使用“+”来编码空间。稍微紧凑的网址。

      基准测试:

      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"
      
      

      【讨论】:

        【解决方案5】:

        注意

        • 这些函数不是用来编码 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
        }
        

        致电replaceUnicodes() --> replaceHtmlEntities() --> toUTF8Encoded()

        【讨论】:

          【解决方案6】:

          在这种情况下,我需要对主机名进行 URL 编码。不要问为什么。作为一个极简主义者和 Perl 粉丝,这就是我的想法。

          url_encode()
            {
            echo -n "$1" | perl -pe 's/[^a-zA-Z0-9\/_.~-]/sprintf "%%%02x", ord($&)/ge'
            }
          

          非常适合我。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-09-15
            • 1970-01-01
            • 2022-10-07
            相关资源
            最近更新 更多