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

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

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

当前回答

对于那些正在寻找不需要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
   '`
}

通过网络上的几个地方和当地的一些试验和错误拼接在一起。效果很好!

其他回答

另一个选择是使用jq:

$ printf %s 'input text'|jq -sRr @uri
input%20text
$ jq -rn --arg x 'input text' '$x|@uri'
input%20text

-r(——raw-output)输出字符串的原始内容,而不是JSON字符串字面量。-n(——null-input)不从STDIN读取输入。

-R(——raw-input)将输入行视为字符串,而不是将其解析为JSON,而-sR(——slurp——raw-input)将输入读入单个字符串。如果你的输入只包含一行,或者你不想用%0A替换换行符,你可以用-Rr替换-sRr:

$ printf %s\\n multiple\ lines of\ text|jq -Rr @uri
multiple%20lines
of%20text
$ printf %s\\n multiple\ lines of\ text|jq -sRr @uri
multiple%20lines%0Aof%20text%0A

或者这个百分比编码所有字节:

xxd -p|tr -d \\n|sed 's/../%&/g'

对于我的一个案例,我发现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方法:

echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"

这可能是最好的一个:

after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")

下面是一个POSIX函数:

url_encode() {
   awk 'BEGIN {
      for (n = 0; n < 125; n++) {
         m[sprintf("%c", n)] = n
      }
      n = 1
      while (1) {
         s = substr(ARGV[1], n, 1)
         if (s == "") {
            break
         }
         t = s ~ /[[:alnum:]_.!~*\47()-]/ ? t s : t sprintf("%%%02X", m[s])
         n++
      }
      print t
   }' "$1"
}

例子:

value=$(url_encode "$2")