ChatGPT解决这个技术问题 Extra ChatGPT

如何对 curl 命令的数据进行 urlencode?

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

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

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

n
neu242

使用curl --data-urlencode;来自 man curl

这将发布数据,类似于其他 --data 选项,不同之处在于它执行 URL 编码。为了符合 CGI, 部分应以名称开头,后跟分隔符和内容规范。

示例用法:

curl \
    --data-urlencode "paramName=value" \
    --data-urlencode "secondParam=value" \
    http://example.com

有关详细信息,请参阅 the man page

这需要 curl 7.18.0 or newer (released January 2008)。使用 curl -V 检查您拥有的版本。

您也可以对 query string 进行编码:

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

似乎只适用于 http POST。此处的文档:curl.haxx.se/docs/manpage.html#--data-urlencode
@StanJames 如果您像这样使用它 curl 也可以为 GET 请求进行编码。 curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
@kberg 实际上,这仅适用于查询数据。 curl 将附加一个“?”其次是 urlencoded 参数。如果您想对某些 url 后缀进行 urlencode(例如某些文档 id 的 CouchDB GET),那么“--data-urlencode”将不起作用。
不适用于 curl --data-urlencode "description=![image]($url)" www.example.com。知道为什么吗? `
@NadavB 逃离"
k
kthompso

这是纯粹的 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 标准在不断发展,并且变得非常微妙。您可以组合在一起的任何实现都不会完全合规,如果您通过一些极端的努力来管理它,它就不会保持合规。


不幸的是,这个脚本在某些字符上失败,例如'é'和'½',分别输出'e%FFFFFFFFFFFFFFCC'和'%FFFFFFFFFFFFFFC2'(我相信每个字符循环的b/c)。
它在 Bash 4.3.11(1) 中对我不起作用。字符串 Jogging «à l'Hèze» 生成无法提供给 JS decodeURIComponentJogging%20%abà%20l%27Hèze%bb :(
在第一个代码块中 printf 的最后一个参数是什么意思?也就是为什么是双引号、单引号、美元符号、字母c、双引号?单引号可以吗?
@ColinFraizer 单引号用于将以下字符转换为其数值。参考。 pubs.opengroup.org/onlinepubs/9699919799/utilities/…
@Matthematics、@dmcontador、@Orwellophile:我的 previous comment 错了。 Solution 使用 xxd 更好,并且在任何情况下都有效(对于任何字符)。我已更新 my script。无论如何,看起来 rawurldecode() 函数运行得非常好。 :)
n
nisetama

另一种选择是使用 jq

$ printf %s 'encode this'|jq -sRr @uri
encode%20this
$ jq -rn --arg x 'encode this' '$x|@uri'
encode%20this

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

-R (--raw-input) 将输入行视为字符串,而不是将它们解析为 JSON,并且 -sR (--slurp --raw-input) 将输入读入单个字符串。如果您的输入仅包含一行,或者您不想将换行符替换为 %0A,则可以将 -sRr 替换为 -Rr

$ 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'

<3 它...应该是顶部 &接受了 IMO(是的,如果您可以告诉 curl 对其进行编码,并且如果 bash 有一个可以接受的内置函数 - 但 jq 似乎很合适,但我远未达到使用此工具的舒适度)
对于任何想知道与我相同的事情的人:@uri 不是变量,而是用于格式化字符串和转义的文字 jq 过滤器;详情见jq manual(抱歉,没有直接链接,需要在页面上搜索@uri...)
xxd 版本正是我一直在寻找的那种东西。即使有点脏,也很短,没有依赖
jq 对 url 编码的示例用法:printf "http://localhost:8082/" | jq -sRr '@uri'
我认为这不是最佳答案的唯一原因是 OP 专门询问了 curl。如果 curl 可以单独完成,您不会循环使用第二个工具 jq。然而,这是 awesome 通用实用程序,用于在脚本或命令行中通过管道输出。
d
dubek

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

...

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

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


URI::Escape 可能未安装,在这种情况下检查我的答案。
我修复了这个问题(使用 echo、管道和 <>),现在即使 $2 包含撇号或双引号,它也可以工作。谢谢!
您也取消了 echovalue="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
Chris Johnsen 的版本更好。我的测试表达式中有 ${True} ,并且通过 echo 使用它会触发 uri_escape / Perl 变量扩展。
@jrw32982 是的,回头看看,用另一种语言来完成这项任务很好。如果可以的话,我会收回我的反对票,但可惜它目前已被锁定。
B
Bruno Bronosky

一种变体,可能很难看,但很简单:

urlencode() {
    local data
    if [[ $# != 1 ]]; then
        echo "Usage: $0 string-to-urlencode"
        return 1
    fi
    data="$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "$1" "")"
    if [[ $? != 3 ]]; then
        echo "Unexpected error" 1>&2
        return 2
    fi
    echo "${data##/?}"
    return 0
}

例如,这里是单行版本(如 Bruno 所建议):

date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-

# If you experience the trailing %0A, use
date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | sed -E 's/..(.*).../\1/'

我认为这是重用 cURL 的 URL 编码的一种非常聪明的方法。
这真是太棒了!我真的希望你把它留下一行,这样人们就可以看到它是多么简单。要对 date 命令的结果进行 URL 编码... date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(您必须 cut 去掉前 2 个字符,因为 curl 的输出在技术上是一个带有查询字符串的相对 URL。)
@BrunoBronosky 您的单行变体很好,但似乎在编码末尾添加了“%0A”。用户当心。功能版好像没有这个问题。
为避免最后出现 %0A,请使用 printf 而不是 echo
一个班轮很棒
j
josch

为完整起见,许多使用 sedawk 的解决方案仅翻译一组特殊字符,因此代码大小相当大,也不翻译应编码的其他特殊字符。

一种安全的 urlencode 方法是只对每个字节进行编码——即使是那些被允许的字节。

echo -ne 'some random\nbytes' | xxd -plain | tr -d '\n' | sed 's/\(..\)/%\1/g'

xxd 在这里注意输入被处理为字节而不是字符。

编辑:

xxd 带有 Debian 中的 vim-common 软件包,我只是在一个没有安装它的系统上,我不想安装它。替代方法是使用 Debian 中 bsdmainutils 软件包中的 hexdump。根据下图,安装 bsdmainutils 和 vim-common 的可能性应该差不多:

http://qa.debian.org/popcon-png.php?packages=vim-common%2Cbsdmainutils&show_installed=1&want_legend=1&want_ticks=1

但是这里有一个使用 hexdump 而不是 xxd 并允许避免 tr 调用的版本:

echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'

xxd -plain 应该发生在 tr -d '\n' 之后!
@qdii 为什么?这不仅会使对换行符进行 urlencode 变得不可能,而且还会错误地将 xxd 创建的换行符插入到输出中。
@josch。这完全是错误的。首先,任何 \n 字符都会被 xxd -plain 翻译成 0a。不要相信我的话,你自己试试吧:echo -n -e '\n' | xxd -plain这证明你的tr -d '\n'在这里没用,因为在xxd -plain之后不可能有任何\n 其次,echo foobar添加了自己的\n字符在字符串的末尾,因此 xxd -plain 没有按预期提供 foobar 而是 foobar\n。然后 xxd -plain 将其转换为一些以 0a 结尾的字符串,使其不适合用户。您可以将 -n 添加到 echo 来解决它。
@qdii 确实 -n 缺少 echo 但 xxd 调用属于 tr -d 调用之前。它属于那里,因此 foobar 中的任何换行符都由 xxd 翻译。 xxd 调用后的 tr -d 用于删除 xxd 生成的换行符。似乎您的 foobar 永远不够长,以至于 xxd 会产生换行符,但对于长输入它会。所以 tr -d 是必要的。与您的假设相反,tr -d 不是从输入中删除换行符,而是从 xxd 输出中删除。我想在输入中保留换行符。您唯一有效的一点是, echo 添加了不必要的换行符。
@qdii 并没有冒犯 - 我只是认为你错了,除了我确实错过的 echo -n
k
kynan

我发现它在 python 中更具可读性:

encoded_value=$(python3 -c "import urllib.parse; print urllib.parse.quote('''$value''')")

三重 ' 确保价值中的单引号不会受到伤害。 urllib 在标准库中。例如,它适用于这个疯狂的(现实世界)网址:

"http://www.rai.it/dl/audio/" "1264165523944Ho servito il re d'Inghilterra - Puntata 7

我在使用三引号时遇到了引号和特殊字符的问题,这似乎对所有内容都有效:encoded_value="$( echo -n "${data}" | python -c "import urllib; import sys; sys.stdout。写(urllib.quote(sys.stdin.read()))")";
Python 3 版本为 encoded_value=$(python3 -c "import urllib.parse; print (urllib.parse.quote('''$value'''))")
urllib.parse.quote 不对正斜杠“/”进行编码。 urlencode() { python3 -c 'import urllib.parse; import sys; print(urllib.parse.quote(sys.argv[1], safe=""))' "$1" }
引用 sys.argv 比将 $value 替换为稍后解析为代码的字符串要安全得多。如果 value 包含 ''' + __import__("os").system("rm -rf ~") + ''' 会怎样?
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
M
MDMower

我发现以下代码片段有助于将其粘贴到程序调用链中,其中 URI::Escape 可能未安装:

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

(source)


为我工作。我将其更改为 perl -lpe ...(字母 ell)。这删除了我需要的尾随换行符。
仅供参考,要反其道而行之,请使用 perl -pe 's/\%(\w\w)/chr hex $1/ge'(来源:unix.stackexchange.com/questions/159253/…
根据具体需要编码的字符,您可以将其简化为 perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge',它允许字母、数字和下划线,但对其他所有字符进行编码。
感谢楼上的回复!由于用例是针对 curl 的:即::/ 不需要编码,所以我在 bashrc/zshrc 中的最终函数是:perl -lpe 's/([^A-Za-z0-9.\/:])/sprintf("%%%02X", ord($1))/seg
@TobiasFeil 它来自标准输入。
P
Piotr Czapla

如果您希望运行 GET 请求并使用纯 curl,只需将 --get 添加到 @Jacob 的解决方案中。

这是一个例子:

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

c
chenzhiwei

这可能是最好的一个:

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

这对我有两个补充:1. 用 -n 替换 -e 以避免在参数末尾添加换行符 2. 在 printf 字符串中添加 '%%' 以在每对前面放置一个 %十六进制数字。
在添加 $ 前括号 after=$(echo -e ... 后工作
请解释这是如何工作的。 od 命令并不常见。
这不适用于 OS X 的 od,因为它使用与 GNU od 不同的输出格式。例如,printf aa|od -An -tx1 -v|tr \ - 使用 OS X 的 od 打印 -----------61--61--------------------------------------------------------,使用 GNU od 打印 -61-61。您可以将 od -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\n 与 OS X 的 od 或 GNU od 一起使用。 xxd -p|sed 's/../%&/g'|tr -d \\n 做同样的事情,尽管 xxd 不在 POSIX 中,但 od 在。
尽管这可能有效,但它会转义每个字符
P
Pokechu22

直接链接到 awk 版本:http://www.shelldorado.com/scripts/cmds/urlencode
我用了很多年,它就像一个魅力

:
##########################################################################
# Title      :  urlencode - encode URL data
# Author     :  Heiner Steven (heiner.steven@odn.de)
# Date       :  2000-03-15
# Requires   :  awk
# Categories :  File Conversion, WWW, CGI
# SCCS-Id.   :  @(#) urlencode  1.4 06/10/29
##########################################################################
# Description
#   Encode data according to
#       RFC 1738: "Uniform Resource Locators (URL)" and
#       RFC 1866: "Hypertext Markup Language - 2.0" (HTML)
#
#   This encoding is used i.e. for the MIME type
#   "application/x-www-form-urlencoded"
#
# Notes
#    o  The default behaviour is not to encode the line endings. This
#   may not be what was intended, because the result will be
#   multiple lines of output (which cannot be used in an URL or a
#   HTTP "POST" request). If the desired output should be one
#   line, use the "-l" option.
#
#    o  The "-l" option assumes, that the end-of-line is denoted by
#   the character LF (ASCII 10). This is not true for Windows or
#   Mac systems, where the end of a line is denoted by the two
#   characters CR LF (ASCII 13 10).
#   We use this for symmetry; data processed in the following way:
#       cat | urlencode -l | urldecode -l
#   should (and will) result in the original data
#
#    o  Large lines (or binary files) will break many AWK
#       implementations. If you get the message
#       awk: record `...' too long
#        record number xxx
#   consider using GNU AWK (gawk).
#
#    o  urlencode will always terminate it's output with an EOL
#       character
#
# Thanks to Stefan Brozinski for pointing out a bug related to non-standard
# locales.
#
# See also
#   urldecode
##########################################################################

PN=`basename "$0"`          # Program name
VER='1.4'

: ${AWK=awk}

Usage () {
    echo >&2 "$PN - encode URL data, $VER
usage: $PN [-l] [file ...]
    -l:  encode line endings (result will be one line of output)

The default is to encode each input line on its own."
    exit 1
}

Msg () {
    for MsgLine
    do echo "$PN: $MsgLine" >&2
    done
}

Fatal () { Msg "$@"; exit 1; }

set -- `getopt hl "$@" 2>/dev/null` || Usage
[ $# -lt 1 ] && Usage           # "getopt" detected an error

EncodeEOL=no
while [ $# -gt 0 ]
do
    case "$1" in
        -l) EncodeEOL=yes;;
    --) shift; break;;
    -h) Usage;;
    -*) Usage;;
    *)  break;;         # First file name
    esac
    shift
done

LANG=C  export LANG
$AWK '
    BEGIN {
    # We assume an awk implementation that is just plain dumb.
    # We will convert an character to its ASCII value with the
    # table ord[], and produce two-digit hexadecimal output
    # without the printf("%02X") feature.

    EOL = "%0A"     # "end of line" string (encoded)
    split ("1 2 3 4 5 6 7 8 9 A B C D E F", hextab, " ")
    hextab [0] = 0
    for ( i=1; i<=255; ++i ) ord [ sprintf ("%c", i) "" ] = i + 0
    if ("'"$EncodeEOL"'" == "yes") EncodeEOL = 1; else EncodeEOL = 0
    }
    {
    encoded = ""
    for ( i=1; i<=length ($0); ++i ) {
        c = substr ($0, i, 1)
        if ( c ~ /[a-zA-Z0-9.-]/ ) {
        encoded = encoded c     # safe character
        } else if ( c == " " ) {
        encoded = encoded "+"   # special handling
        } else {
        # unsafe character, encode it as a two-digit hex-number
        lo = ord [c] % 16
        hi = int (ord [c] / 16);
        encoded = encoded "%" hextab [hi] hextab [lo]
        }
    }
    if ( EncodeEOL ) {
        printf ("%s", encoded EOL)
    } else {
        print encoded
    }
    }
    END {
        #if ( EncodeEOL ) print ""
    }
' "$@"

是否有一个简单的变体来获得 UTF-8 编码而不是 ASCII?
d
davidchambers

这是一个不调用任何外部程序的 Bash 解决方案:

uriencode() {
  s="${1//'%'/%25}"
  s="${s//' '/%20}"
  s="${s//'"'/%22}"
  s="${s//'#'/%23}"
  s="${s//'$'/%24}"
  s="${s//'&'/%26}"
  s="${s//'+'/%2B}"
  s="${s//','/%2C}"
  s="${s//'/'/%2F}"
  s="${s//':'/%3A}"
  s="${s//';'/%3B}"
  s="${s//'='/%3D}"
  s="${s//'?'/%3F}"
  s="${s//'@'/%40}"
  s="${s//'['/%5B}"
  s="${s//']'/%5D}"
  printf %s "$s"
}

这在 bash 版本之间表现不同。在 RHEL 6.9 上,bash 是 4.1.2,它包含单引号。虽然 Debian 9 和 bash 4.4.12 可以使用单引号。对我来说,删除单引号使其适用于两者。 s="${s//','/%2C}"
我更新了答案以反映您的发现,@muni764。
只是一个警告...这不会对字符 á 之类的内容进行编码
C
Cody Gray
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 tab 认为它会变成空格


我觉得这不是推荐的方法。
请解释一下你的感受....因为我所说的有效并且我已经在几个脚本中使用过它,所以我知道它适用于我列出的所有字符。所以请解释为什么有人不会使用我的代码并使用 perl,因为它的标题是“来自 bash 脚本的 URLEncode”而不是 perl 脚本。
有时不需要珍珠溶液,因此可以派上用场
这不是推荐的方法,因为黑名单是不好的做法,无论如何这对 unicode 不友好。
这是与 cat file.txt 兼容的最友好的解决方案
D
Darren Weber

从 shell 脚本中使用 php:

value="http://www.google.com"
encoded=$(php -r "echo rawurlencode('$value');")
# encoded = "http%3A%2F%2Fwww.google.com"
echo $(php -r "echo rawurldecode('$encoded');")
# returns: "http://www.google.com"

http://www.php.net/manual/en/function.rawurlencode.php http://www.php.net/manual/en/function.rawurldecode.php


t
thecoshman

如果你不想依赖 Perl,你也可以使用 sed。这有点混乱,因为每个字符都必须单独转义。创建一个包含以下内容的文件并将其命名为 urlencode.sed

s/%/%25/g
s/ /%20/g
s/ /%09/g
s/!/%21/g
s/"/%22/g
s/#/%23/g
s/\$/%24/g
s/\&/%26/g
s/'\''/%27/g
s/(/%28/g
s/)/%29/g
s/\*/%2a/g
s/+/%2b/g
s/,/%2c/g
s/-/%2d/g
s/\./%2e/g
s/\//%2f/g
s/:/%3a/g
s/;/%3b/g
s//%3e/g
s/?/%3f/g
s/@/%40/g
s/\[/%5b/g
s/\\/%5c/g
s/\]/%5d/g
s/\^/%5e/g
s/_/%5f/g
s/`/%60/g
s/{/%7b/g
s/|/%7c/g
s/}/%7d/g
s/~/%7e/g
s/      /%09/g

要使用它,请执行以下操作。

STR1=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f1)
STR2=$(echo "https://www.example.com/change&$ ^this to?%checkthe@-functionality" | cut -d\? -f2)
OUT2=$(echo "$STR2" | sed -f urlencode.sed)
echo "$STR1?$OUT2"

这会将字符串拆分为需要编码的部分,以及可以的部分,对需要它的部分进行编码,然后重新缝合在一起。

为方便起见,您可以将其放入 sh 脚本中,也许需要一个参数进行编码,将其放在您的路径上,然后您可以调用:

urlencode https://www.exxample.com?isThisFun=HellNo

source


W
Wolfgang Fahl

Python 3 基于@sandro 2010 年的好回答:

echo "Test & /me" | python -c "import urllib.parse;print (urllib.parse.quote(input()))"

测试%20%26%20/我


a
ataylor

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

从网络上的几个地方和一些本地试验和错误缝合在一起。效果很好!


k
kev

uni2ascii 非常方便:

$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C

这不适用于 inside ASCII 范围内的字符,需要引用,例如 % 和空格(最后可以使用 -s 标志补救)
K
Klaus

您可以在 perl 中模拟 javascript 的 encodeURIComponent。这是命令:

perl -pe 's/([^a-zA-Z0-9_.!~*()'\''-])/sprintf("%%%02X", ord($1))/ge'

您可以在 .bash_profile 中将其设置为 bash 别名:

alias encodeURIComponent='perl -pe '\''s/([^a-zA-Z0-9_.!~*()'\''\'\'''\''-])/sprintf("%%%02X",ord($1))/ge'\'

现在您可以通过管道进入 encodeURIComponent

$ echo -n 'hèllo wôrld!' | encodeURIComponent
h%C3%A8llo%20w%C3%B4rld!

M
Marcus Müller

简单的 PHP 选项:

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

m
masterxilo

这个基于 nodejs 的答案将在标准输入上使用 encodeURIComponent

uriencode_stdin() {
    node -p 'encodeURIComponent(require("fs").readFileSync(0))'
}

echo -n $'hello\nwörld' | uriencode_stdin
hello%0Aw%C3%B6rld


那里最好的版本;)
N
Nestor Urquiza

什么会比 javascript 更好地解析 URL?

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

超出操作问题范围。不是 bash,不是 curl。即使我确定节点可用时效果很好。
为什么不赞成这个而不是 python/perl 的答案?此外,这如何不响应原始问题“如何对 curl 命令的数据进行 urlencode?”。这可以从 bash 脚本中使用,并且可以将结果提供给 curl 命令。
我也对其他人投了反对票。问题是如何在 bash 脚本中执行此操作。如果使用其他语言,如 node/js、python 或 perl,则无需直接使用 curl。
虽然我没有费心去投反对票,但这个命令的问题是它需要正确转义数据才能在 javascript 中使用。就像用单引号和一些反斜杠疯狂尝试一样。如果你想使用节点,你最好从标准输入中读取 node -p 'encodeURIComponent(require("fs").readFileSync(0))' 之类的东西
如果您从 STDIN 输入数据,请注意 @MichaelKrelin-hacker 的解决方案,确保不要包含尾随换行符。例如,echo | ... 是错误的,而 echo -n | ... 禁止换行。
d
davidchambers

这是节点版本:

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

如果字符串中有任何其他字符在单引号之间无效,例如单个反斜杠或换行符,这不会中断吗?
好点子。如果我们要在 Bash 中转义所有有问题的字符,我们不妨直接执行替换并完全避免 node。我发布了一个仅限 Bash 的解决方案。 :)
页面其他地方的这个变体通过从 STDIN 读取值来避免引用问题:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
D
Dylan

问题是关于在 bash 中执行此操作,并且不需要 python 或 perl,因为实际上有一个命令可以完全满足您的要求 - “urlencode”。

value=$(urlencode "${2}")

这也好得多,例如,上面的 perl 答案没有正确编码所有字符。用你从 Word 中得到的长破折号试试,你得到了错误的编码。

请注意,您需要安装“gridsite-clients”才能提供此命令。


我的 bash (GNU 3.2) 版本没有 urlencode。你用的是什么版本?
我有 4.3.42,但 urlencode 命令由“gridsite-clients”提供。尝试安装它,你应该没问题。
所以你的答案并不比任何需要安装其他东西的更好(python,perl,lua,......)
除了它只需要安装一个实用程序而不是整个语言(和库)之外,它还超级简单明了,可以看到它在做什么。
提供此命令的包/项目页面的链接会很有用。
j
jan halfar

另一种php方法:

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

echo 将附加一个换行符(十六进制 0xa)。要阻止它这样做,请使用 echo -n
Z
Zombo

这是执行此操作的 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")

k
k107

Ruby,为了完整性

value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"

n
nulleight

这是我的嵌入式系统busybox ash shell版本,我最初采用了Orwellophile的变体:

urlencode()
{
    local S="${1}"
    local encoded=""
    local ch
    local o
    for i in $(seq 0 $((${#S} - 1)) )
    do
        ch=${S:$i:1}
        case "${ch}" in
            [-_.~a-zA-Z0-9]) 
                o="${ch}"
                ;;
            *) 
                o=$(printf '%%%02x' "'$ch")                
                ;;
        esac
        encoded="${encoded}${o}"
    done
    echo ${encoded}
}

urldecode() 
{
    # urldecode <string>
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}

C
Community

这是使用 Lua 的单行转换,类似于 blueyed's answer,除了所有 RFC 3986 Unreserved Characters 未编码(如 this answer):

url=$(echo 'print((arg[1]:gsub("([^%w%-%.%_%~])",function(c)return("%%%02X"):format(c:byte())end)))' | lua - "$1")

此外,您可能需要确保字符串中的换行符从 LF 转换为 CRLF,在这种情况下,您可以在百分比编码之前在链中插入 gsub("\r?\n", "\r\n")

这是一个变体,在 the non-standard style of application/x-www-form-urlencoded 中,它执行换行规范化,以及将空格编码为“+”而不是“%20”(可能使用类似的技术将其添加到 Perl 片段中)。

url=$(echo 'print((arg[1]:gsub("\r?\n", "\r\n"):gsub("([^%w%-%.%_%~ ]))",function(c)return("%%%02X"):format(c:byte())end):gsub(" ","+"))' | lua - "$1")

a
ajaest

安装 php 我使用这种方式:

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

当它包含单引号 ' 字符时可能无法处理 $DATA