我试图写一个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选项卡,认为它会把它变成空格

其他回答

有什么能比javascript更好地解析url呢?

node -p "encodeURIComponent('$url')"

这是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++

下面是纯BASH的答案。

更新:由于已经讨论了许多变化,我已经把这个放在https://github.com/sfinktah/bash/blob/master/rawurlencode.inc.sh上,任何人都可以发布一个PR。

注意:这个解决方案并不打算编码unicode或多字节字符——这完全超出了BASH的简单本机功能。它仅用于编码那些在POST或GET请求中会破坏参数传递的符号,例如。'&', '='等等。

非常重要的提示:不要尝试用任何语言编写你自己的UNICODE转换函数。见答案结尾。

rawurlencode() {
  local string="${1}"
  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
  echo "${encoded}"    # You can either set a return variable (FASTER) 
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

你可以在两种情况下使用它:

easier:  echo http://url/q?=$( rawurlencode "$args" )
faster:  rawurlencode "$args"; echo http://url/q?${REPLY}

(编辑)

下面是匹配的rawurldecode()函数,它非常棒。

# Returns a string in which the sequences with percent (%) signs followed by
# two hex digits have been replaced with literal characters.
rawurldecode() {

  # This is perhaps a risky gambit, but since all escape characters must be
  # encoded, we can replace %NN with \xNN and pass the lot to printf -b, which
  # will decode hex for us

  printf -v REPLY '%b' "${1//%/\\x}" # You can either set a return variable (FASTER)

  echo "${REPLY}"  #+or echo the result (EASIER)... or both... :p
}

有了匹配集,我们现在可以执行一些简单的测试:

$ diff rawurlencode.inc.sh \
        <( rawurldecode "$( rawurlencode "$( cat rawurlencode.inc.sh )" )" ) \
        && echo Matched

Output: Matched

如果你真的觉得你需要一个外部工具(好吧,它会快得多,可能会处理二进制文件之类的…)我在我的OpenWRT路由器上发现了这个…

replace_value=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed)

url_escape的地方。Sed是一个包含以下规则的文件:

# sed url escaping
s:%:%25:g
s: :%20:g
s:<:%3C:g
s:>:%3E:g
s:#:%23:g
s:{:%7B:g
s:}:%7D:g
s:|:%7C:g
s:\\:%5C:g
s:\^:%5E:g
s:~:%7E:g
s:\[:%5B:g
s:\]:%5D:g
s:`:%60:g
s:;:%3B:g
s:/:%2F:g
s:?:%3F:g
s^:^%3A^g
s:@:%40:g
s:=:%3D:g
s:&:%26:g
s:\$:%24:g
s:\!:%21:g
s:\*:%2A:g

虽然在BASH中编写这样一个能够处理UTF-8输入的脚本(可能使用xxd和一个非常长的规则集)并不是不可能的,但是有更快更可靠的方法。试图将UTF-8解码为UTF-32是一项非常重要的任务,要做到准确,尽管很容易做得不准确,以至于您认为它有效,直到有一天它不起作用。

甚至Unicode联盟在发现样本代码不再100%与实际标准兼容后,也删除了样本代码。

Unicode标准在不断发展,变得非常微妙。您可以拼凑的任何实现都不会具有适当的遵从性,如果您通过一些极端的努力来管理它,它也不会保持遵从性。

这是节点版本:

uriencode() {
  node -p "encodeURIComponent('${1//\'/\\\'}')"
}

另一个选择是使用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'