如何为curl命令添加数据?


319

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

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

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

另请参阅:如何在Shell中解码URL编码的字符串?用于非卷曲解决方案。
kenorb'3

Answers:


394

使用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 -G \
    --data-urlencode "p1=value 1" \
    --data-urlencode "p2=value 2" \
    http://example.com
    # http://example.com?p1=value%201&p2=value%202

5
似乎仅适用于http POST。此处的文档:curl.haxx.se/docs/manpage.html#
Stan James

82
@StanJames如果像这样使用它,curl也可以对GET请求进行编码。 curl -G --data-urlencode "blah=df ssdf sdf" --data-urlencode "blah2=dfsdf sdfsd " http://whatever.com/whatever
kberg 2012年

13
@kberg实际上,这仅适用于查询数据。curl会附加一个“?” 其次是urlencoded参数。如果您要对某些网址后缀进行urlencode(例如用于某些文档ID的CouchDB GET),则'--data-urlencode'将不起作用。
散景

1
不适用于curl --data-urlencode "description=![image]($url)" www.example.com。知道为什么吗?`
Khurshid Alam

1
@NadavB摆脱"
大酒杯

179

这是纯BASH答案。

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

4
不幸的是,此脚本在某些字符(例如'é'和'½')上失败,分别输出了'e%FFFFFFFFFFFFFFFFCC'和'%FFFFFFFFFFFFFFC2'(我相信每个字符循环的b / c)。
Matthemattics 2014年

1
它在Bash 4.3.11(1)中对我不起作用。Jogging «à l'Hèze»生成的字符串Jogging%20%abà%20l%27Hèze%bb无法馈送到JS decodeURIComponent:(
dmcontador

2
在第一段代码中,printf的最后一个参数是什么意思?也就是说,为什么要使用双引号,单引号,美元符号,字母c和双引号?单引号可以吗?
科林·弗雷泽

1
@dmcontador-它只是一个不起眼的bash脚本,没有多字节字符或unicode的概念。当看到类似ń(\u0144)的字符时,它将天真地输出%144,144(\u2561)将输出为%2561。正确的原始ururlencoded答案分别是%C5%84%0A和%E2%95%A1。
Orwellophile

1
@ColinFraizer单引号用于将以下字符转换为其数值。参考 pubs.opengroup.org/onlinepubs/9699919799/utilities/...
山姆

94

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

...

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

编辑:修复引用问题,如克里斯·约翰森在评论中所建议。谢谢!


2
URI :: Escape可能未安装,请检查我的答案。
2009年

我修复了这个问题(use echo,pipe和<>),现在即使$ 2包含撇号或双引号,它也可以工作。谢谢!
dubek 2010年

9
您也可以echovalue="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
删除

1
克里斯·约翰森的版本更好。我在测试表达式中有$ {True},并通过回显使用了它,使uri_escape / Perl变量扩展跳了起来。
mm2001年

1
@ jrw32982是的,回头看一下,用另一种语言可以完成此任务是好的。如果我可以,我会收回我的downvote,但可惜它目前锁定。
thecoshman

69

另一种选择是使用jq(作为过滤器):

jq -sRr @uri

-R--raw-input)将输入行视为字符串,而不是将其解析为JSON,并且-sR--slurp --raw-input)将输入读取为单个字符串。-r--raw-output)输出字符串的内容,而不是JSON字符串文字。

如果输入不是另一个命令的输出,则可以将其存储在jq字符串变量中:

jq -nr --arg v "my shell string" '$v|@uri'

-n--null-input)不读取输入,而是--arg name value将其作为字符串存储value在变量name中。在过滤器中$name(用单引号引起来,以避免被外壳扩展),引用变量name

包装为Bash函数,它将变为:

function uriencode { jq -nr --arg v "$1" '$v|@uri'; }

或此百分比编码所有字节:

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

3
<3它...应该是顶级且被接受的IMO(是的,如果您可以告诉您curl进行编码,并且bash具有可以接受的内置功能-但jq似乎是合适的选择,但我远未达到舒适度)此工具)
nhed

5
对于任何想和我一样的人:@uri不是变量,而是用于格式化字符串和转义的文字jq过滤器;有关详细信息,请参见jq手册(对不起,没有直接链接,需要@uri在页面上搜索...)
ssc

xxd版本只是我一直在寻找的那种东西。即使有点脏,它也很短且没有依赖项
Rian Sanderson

1
jq进行网址编码的示例用法:printf "http://localhost:8082/" | jq -sRr '@uri'
Ashutosh Jindal

67

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

urlencode的一种安全方法是仅对每个字节进行编码-即使是允许的字节也是如此。

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

xxd在这里要注意,输入是按字节而不是字符处理的。

编辑:

xxd随Debian一起提供了vim-common软件包,而我只是在没有安装它的系统上,所以我不想安装它。替代方法是hexdump从Debian中的bsdmainutils 包中使用。根据下图,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'

1
xxd -plain应该在之后发生tr -d '\n'
qdii 2012年

3
@qdii为什么?这不仅使无法对换行符进行urlencode,而且还会错误地将xxd创建的换行符插入输出中。
josch 2012年

1
@josch。这是完全错误的。首先,任何\n字符都会被翻译xxd -plain0a。不要相信我的话,请自己尝试:echo -n -e '\n' | xxd -plain这证明您tr -d '\n'在这里是无用的,因为\nxxd -plain 第二个之后不能有任何其他内容,在字符串的末尾echo foobar添加自己\n的字符,因此xxd -plain不是foobar按预期的方式而是通过来提供foobar\n。然后xxd -plain 将其转换为以结尾的字符串0a,从而使其不适合用户。你可以添加-necho解决它。
qdii 2012年

6
@qdii实际上-n缺少回显,但该xxd调用属于该调用的前面tr -d。它属于此处,因此其中的所有换行符都由来foobar翻译xxd。该调用tr -d之后的操作xxd是删除xxd产生的换行符。似乎您永远没有足够长的foobar来xxd产生换行符,但是对于长输入而言,它将如此。因此,这tr -d是必要的。与您的假设相反,tr -d不是从输入中删除换行符而是从xxd输出中删除换行符。我想在输入中保留换行符。您唯一有效的一点是,该回显会添加不必要的换行符。
josch 2012年

1
@qdii并没有采取进攻-我认为你是错的,除了echo -n我确实失踪
josch

62

变体之一可能很丑陋,但很简单:

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

1
我认为这是重用cURL的URL编码的非常聪明的方法。
solidsnack 2012年

13
这绝对是太棒了!我真希望您能将其保留一行,以便人们可以看到它实际上是多么简单。要对date命令的结果进行URL编码…… date | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | cut -c 3-(您必须cut将前2个字符放掉,因为​​curl的输出在技术上是带有查询字符串的相对URL。)
Bruno Bronosky

2
@BrunoBronosky您的单线变体很好,但似乎在编码的末尾添加了“%0A”。用户要当心。函数版本似乎没有此问题。
levigroker

7
为避免%0A最后出现,请使用printf代替echo
kenorb

2
一个班轮很棒
Stephen Blum

49

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

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

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

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

2
我在使用引号和特殊字符使用三引号时遇到了一些麻烦,这似乎基本上适用于所有内容:encode_value =“ $(echo -n” $ {data}“ | python -c” import urllib; import sys; sys.stdout。 write(urllib.quote(sys.stdin.read()))“)”;
停止诽谤Monica Cellio 2011年

Python 3版本为encoded_value=$(python3 -c "import urllib.parse; print (urllib.parse.quote('''$value'''))")
Creshal

1
python -c 'import urllib, sys; sys.stdout.writelines(urllib.quote_plus(l, safe="/\n") for l in sys.stdin)'几乎没有报价问题,并且应该具有较高的内存/速度效率(未检查,省却了)
Alois Mahdal

2
引用sys.argv而不是替换$value为以后解析为代码的字符串会更安全。如果value包含该''' + __import__("os").system("rm -rf ~") + '''怎么办?
查尔斯·达菲

2
python -c "import urllib;print urllib.quote(raw_input())" <<< "$data"
Rockallite

30

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

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

来源


4
为我工作。我将其更改为perl -lpe ...(字母ell)。这删除了结尾的换行符,这是我需要的。
JohnnyLambada

2
仅供参考,为此,请使用perl -pe 's/\%(\w\w)/chr hex $1/ge'(来源:unix.stackexchange.com/questions/159253/…
Sridhar Sarnobat,2015年

2
根据具体需要编码的字符,可以将perl -pe 's/(\W)/sprintf("%%%02X", ord($1))/ge'其简化为允许字母,数字和下划线,但对其他所有字符进行编码。
罗布鲁

23

如果您想运行GET请求并使用纯卷曲,只需添加--get到@Jacob的解决方案中即可。

这是一个例子:

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

15

直接链接到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的简单变体?
avgvstvs 2015年

15

这可能是最好的一种:

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

这对我有用,有两个附加功能:1.用-n替换-e以避免在参数末尾添加换行符;并且2.在printf字符串中添加'%%'以在每个字符串对的前面放置一个%十六进制数字。
罗布·法根

在添加$前面的括号后进行工作 after=$(echo -e ...
Roman Rhrn Nesterov

1
请解释这是如何工作的。该od命令不常见。
马克·斯托斯伯格

这不适用于OS X,od因为它使用的输出格式与GNU不同od。例如,使用OS X 和GNU进行printf aa|od -An -tx1 -v|tr \ -打印。您可以使用OS X 或GNU 。即使不在POSIX中,也可以做到相同的事情。-----------61--61--------------------------------------------------------od-61-61odod -An -tx1 -v|sed 's/ */ /g;s/ *$//'|tr \ %|tr -d \\nododxxd -p|sed 's/../%&/g'|tr -d \\nxxdod
nisetama

2
尽管这可能行得通,但它可以避开每个角色
查理(Charlie)

11

这是一个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"
}

4
bash版本之间的行为有所不同。在RHEL 6.9上,bash为4.1.2,其中包括单引号。Debian 9和bash 4.4.12可以使用单引号。对我来说,删除单引号会使它同时起作用。s =“ $ {s //','/%2C}”
muni764

1
我更新了答案以反映您的发现,@ muni764。
davidchambers,

只是警告...这不会对字符进行编码á
diogovk

10
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中。BTW并未包含sed选项卡,以为它会将其转换为空格


5
我觉得这不是推荐的方法。
科迪灰色

2
请解释一下您的感觉...。因为我所说的有效,并且我在多个脚本中使用了它,所以我知道它适用于列出的所有字符。因此,请解释为什么有人不使用我的代码而使用perl,因为它的标题是“来自bash脚本的URLEncode”而不是perl脚本。
manoflinux 2011年

有时不需要珍珠溶液,因此可以派上用场
Yuval Rimar

3
不建议这样做,因为黑名单是一种不好的做法,而且对unicode还是不利的。
Ekevoo 2011年

这是与cat file.txt兼容的最友好的解决方案
mrwaim


7

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

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


7

uni2ascii非常方便:

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

2
这不适用于ASCII范围的字符,需要加引号,例如%和空格(最后可以用-s标志纠正)
Boldewyn

7

如果您不想依赖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

资源


7

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

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

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

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!

6

这是节点版本:

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

1
如果字符串中还有其他在单引号之间引起无效的字符(例如单反斜杠或换行符),是否不会中断?
Stuart P. Bentley

好点子。如果我们要摆脱Bash中所有有问题的字符的麻烦,我们不妨直接执行替换操作并node完全避免。我发布了仅Bash的解决方案。:)
davidchambers

1
在页面其他地方找到的该变体通过从STDIN读取值避免了报价问题:node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Mark Stosberg,

6

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

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

这也更好,例如,上面的perl答案不能正确编码所有字符。尝试使用从Word获得的长破折号,并且编码错误。

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


1
我的bash版本(GNU 3.2)没有urlencode。您使用的是哪个版本?
Sridhar Sarnobat,2015年

1
我有4.3.42,但是urlencode命令由“ gridsite-clients”提供。尝试安装它,应该没问题。
迪伦2015年

5
因此,您的回答并不比要求安装其他任何东西(python,perl,lua等)的
要好

除了只需要安装一个实用程序而不是安装整个语言(和库)外,再加上超级简单明了的功能即可查看其功能。
迪伦

首先提供该命令的包/项目页面链接将很有用。
Doron Behar

6

简单的PHP选项:

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

4

Ruby,为了完整性

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

4

另一个php方法:

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

2
echo将附加一个换行符(hex 0xa)。要停止这样做,请使用echo -n
Mathew Hall

3

这是我用于嵌入式系统的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}"
}

2

这是一个POSIX函数可以做到这一点:

encodeURIComponent() {
  awk 'BEGIN {while (y++ < 125) z[sprintf("%c", y)] = y
  while (y = substr(ARGV[1], ++j, 1))
  q = y ~ /[[:alnum:]_.!~*\47()-]/ ? q y : q sprintf("%%%02X", z[y])
  print q}' "$1"
}

例:

value=$(encodeURIComponent "$2")

资源


2

这是一个使用Lua的单行转换,类似于blueyed的答案,但所有RFC 3986保留字符均未编码(类似于此答案):

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

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

这是一个变体,它以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")

1

安装了PHP,我使用这种方式:

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

1

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

1

什么比URL解析URL更好?

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

超出操作问题范围。不重击,不卷曲。即使我确定节点可用也可以很好地工作。
Cyrille Pontvieux

为什么要对此投票而不是python / perl答案?此外,这如何不响应原始问题“如何为curl命令添加数据?”。可以从bash脚本中使用它,并将结果提供给curl命令。
内斯特·乌尔基萨

我也否决了其他人。问题是如何在bash脚本中执行此操作。如果使用其他语言(例如node / js,python或perl),则无需直接使用curl。
Cyrille Pontvieux

2
虽然我不费吹灰之力,但是此命令的问题在于它需要正确地转义数据才能在javascript中使用。像尝试使用单引号和一些反斜杠疯狂一样。如果要使用节点,则最好从stdin中读取内容,例如node -p 'encodeURIComponent(require("fs").readFileSync(0))'
Michael Krelin-hacker

1
如果要从STDIN传递数据,请谨慎使用@ MichaelKrelin-hacker的解决方案,确保不要包含结尾的换行符。例如,echo | ...是错误的,而echo -n | ...禁止换行。
马克·斯托斯伯格

0

以下内容基于Orwellophile的答案,但通过设置LC_ALL = C(来自vte.sh的技巧)解决了注释中提到的多字节错误。我以适合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}"
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.