我正在尝试编写一个用于测试的 bash 脚本,该脚本接受一个参数并通过 curl 将其发送到网站。我需要对值进行 url 编码以确保正确处理特殊字符。做这个的最好方式是什么?
到目前为止,这是我的基本脚本:
#!/bin/bash
host=${1:?'bad host'}
value=$2
shift
shift
curl -v -d "param=${value}" http://${host}/somepath $@
使用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
更新:由于已经讨论了许多更改,因此我将其放在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 标准在不断发展,并且变得非常微妙。任何你可以拼凑起来的实现都不会完全合规,如果你通过一些极端的努力来管理它,它就会保持合规。
另一种选择是使用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
) 将输入读入单个字符串。如果您的输入仅包含一行,或者您不想用 替换换行符,则可以替换-sRr
为:-Rr
%0A
$ 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'
在 bash 脚本的第二行使用 Perl 的URI::Escape
模块和函数:uri_escape
...
value="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "$2")"
...
编辑:修复引用问题,正如 Chris Johnsen 在评论中所建议的那样。谢谢!
一种变体,可能很难看,但很简单:
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/'
为了完整起见,许多解决方案使用sed
或awk
仅翻译一组特殊字符,因此代码大小相当大,也不翻译应编码的其他特殊字符。
一种安全的 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 的可能性应该差不多:
但尽管如此,这里有一个版本使用hexdump
而不是xxd
允许避免tr
调用:
echo -ne 'some random\nbytes' | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g'
我发现它在 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
我发现以下代码片段有助于将其粘贴到程序调用链中,其中 URI::Escape 可能未安装:
perl -p -e 's/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg'
(来源)
如果您希望运行GET
请求并使用纯 curl,只需添加--get
到 @Jacob 的解决方案即可。
这是一个例子:
curl -v --get --data-urlencode "access_token=$(cat .fb_access_token)" https://graph.facebook.com/me/feed
这可能是最好的一个:
after=$(echo -e "$before" | od -An -tx1 | tr ' ' % | xargs printf "%s")
直接链接到 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 ""
}
' "$@"
这是一个不调用任何外部程序的 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"
}
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 认为它会变成空格
从 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"
如果你不想依赖 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
Python 3 基于@sandro 2010 年的好回答:
echo "Test & /me" | python -c "import urllib.parse;print (urllib.parse.quote(input()))"
测试%20%26%20/我
对于那些正在寻找不需要 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
'`
}
从网络上的几个地方和一些本地试验和错误缝合在一起。效果很好!
uni2ascii非常方便:
$ echo -ne '你好世界' | uni2ascii -aJ
%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C
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!
简单的 PHP 选项:
echo 'part-that-needs-encoding' | php -R 'echo urlencode($argn);'
问题是关于在 bash 中执行此操作,并且不需要 python 或 perl,因为实际上有一个命令可以完全满足您的要求 - “urlencode”。
value=$(urlencode "${2}")
这也好得多,例如,上面的 perl 答案没有正确编码所有字符。用你从 Word 中得到的长破折号试试,你得到了错误的编码。
请注意,您需要安装“gridsite-clients”才能提供此命令。
这个基于 nodejs 的答案将在标准输入上使用encodeURIComponent:
uriencode_stdin() {
node -p 'encodeURIComponent(require("fs").readFileSync(0))'
}
echo -n $'hello\nwörld' | uriencode_stdin
hello%0Aw%C3%B6rld
这是节点版本:
uriencode() {
node -p "encodeURIComponent('${1//\'/\\\'}')"
}
这是执行此操作的 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")
什么会比 javascript 更好地解析 URL?
node -p "encodeURIComponent('$url')"
Ruby,为了完整性
value="$(ruby -r cgi -e 'puts CGI.escape(ARGV[0])' "$2")"
另一种php方法:
echo "encode me" | php -r "echo urlencode(file_get_contents('php://stdin'));"
这是我的嵌入式系统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}"
}
这是使用 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")
在百分比编码之前在链中插入 a。
这是一个变体,在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")
安装 php 我使用这种方式:
URL_ENCODED_DATA=`php -r "echo urlencode('$DATA');"`
这是 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++
以下内容基于 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}"
}
对于我的一个案例,我发现 NodeJSurl
库有最简单的解决方案。当然是 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
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”。
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
}
作为一个小额外,它使用“+”来编码空间。稍微紧凑的 URL。
基准:
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"
笔记
#!/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/"/%22/g; s/</%3C/g; s/>/%3E/g; s/ /%A0/g; s/¢/%A2/g; s/£/%A3/g; s/¥/%A5/g; s/©/%A9/g; s/®/%AE/g; s/&/\&/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()
在这种情况下,我需要对主机名进行 URL 编码。不要问为什么。作为一个极简主义者和 Perl 粉丝,这就是我想出的。
url_encode()
{
echo -n "$1" | perl -pe 's/[^a-zA-Z0-9\/_.~-]/sprintf "%%%02x", ord($&)/ge'
}
非常适合我。