Bash 如何在shell中解码URL编码的字符串?

Bash 如何在shell中解码URL编码的字符串?,bash,shell,awk,sed,urldecode,Bash,Shell,Awk,Sed,Urldecode,我有一个文件,其中包含编码的用户代理列表。 例如: $ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; } 我想要一个shell脚本,它可以读取此文件并使用解码字符串写入新文件 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en $ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; } 我一直在尝

我有一个文件,其中包含编码的用户代理列表。 例如:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
我想要一个shell脚本,它可以读取此文件并使用解码字符串写入新文件

Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
我一直在尝试使用这个例子来让它运行起来,但到目前为止它还不起作用

$ echo -e "$(echo "%31+%32%0A%33+%34" | sed 'y/+/ /; s/%/\\x/g')"
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
我的脚本看起来像:

#!/bin/bash
for f in *.log; do
  echo -e "$(cat $f | sed 'y/+/ /; s/%/\x/g')" > y.log
done
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
正如评论中所说,
\x
“应该是[double-]转义的”

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
我不会将Bash和sed混为一谈,而是用Python来完成这一切。下面是一个大致的方法:

#!/usr/bin/env python

import glob
import os
import urllib

for logfile in glob.glob(os.path.join('.', '*.log')):
    with open(logfile) as current:
        new_log_filename = logfile + '.new'
        with open(new_log_filename, 'w') as new_log_file:
            for url in current:
                unquoted = urllib.unquote(url.strip())
                new_log_file.write(unquoted + '\n')
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

这似乎对我起了作用

#!/bin/bash
urldecode(){
  echo -e "$(sed 's/+/ /g;s/%\(..\)/\\x\1/g;')"
}

for f in /opt/logs/*.log; do
    name=${f##/*/}
    cat $f | urldecode > /opt/logs/processed/$HOSTNAME.$name
done
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
将“+”替换为空格,将%符号替换为“\x”转义符,并让echo使用“-e”选项解释\x转义符不起作用。由于某些原因,cat命令正在将%符号打印为其自己的编码形式%25。所以sed只是用\x25替换了%25。当使用-e选项时,它只是将\x25计算为%,输出与原始值相同

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
跟踪:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
原创:Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
sed:Mozilla\x252F5.0\x2520\x2528Macintosh\x253B\x2520U\x253B\x2520Intel\x2520Mac\x2520OS\x2520X\x252010.6\x253B\x2520en

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
echo-e:Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
修复:基本上忽略sed中%后面的2个字符

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
sed:Mozilla\x2F5.0\x20\x28Macintosh\x3B\x20U\x3B\x20Intel\x20Mac\x20OS\x20X\x2010.6\x3B\x20en

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
echo-e:Mozilla/5.0(Macintosh;U;Intel Mac OS X 10.6;en

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

在经过大量测试后,我不确定这会导致什么复杂情况,但目前仍然有效。

面对类似的问题,我最初的想法是在读取stdin或类似内容的脚本中使用PHP的urldecode,但后来我发现了这个想法。所有的答案似乎都有很多文本,但没有真正的解决方案。尽管这个想法很好,而且工作起来非常容易:

$ mpc | sed -e '1! d'
http://e.org/play.php?name=/Black%20Sun%20Empire%20-%20Sideways%20%28Feat.%20Illy%20Emcee%29

$ basename "$(echo -e `mpc | sed -e '1! d' -e 's/%/\\\\x/g'`)"
Black Sun Empire - Sideways (Feat. Illy Emcee)
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

使其工作的关键是双转义\x(这已经提到过)。

在本机Bash()中执行此操作的Bash脚本:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

如果您的服务器上安装了php,您可以很容易地使用url编码字符串“cat”甚至“tail”任何文件

tail -f nginx.access.log | php -R 'echo urldecode($argn)."\n";'
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

如果您是一名python开发人员,这可能更可取:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
对于Python3.x(默认值):

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
对于Python2.x(已弃用):

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

非常擅长处理URL解析

这里有一个在纯bash中完成的解决方案,其中输入和输出是bash变量。它将“+”解码为一个空格,并处理“%20”空格以及其他编码字符

#!/bin/bash
#here is text that contains both '+' for spaces and a %20
text="hello+space+1%202"
decoded=$(echo -e `echo $text | sed 's/+/ /g;s/%/\\\\x/g;'`)
echo decoded=$decoded
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
perl-pi.back-e'y/+/;s/%([\da-f]{2})/packh2,$1/gie./*.log
使用
-i
将文件更新到位(一些
sed
实现借用了
perl
),并使用
.back
作为备份扩展名

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
s/x/y/e
y
perl代码的求值替换
x

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
本例中的perl代码使用
pack
$1
中捕获的十六进制数(regexp中的第一个括号对)打包为相应的字符

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
pack
的替代方法是使用
chr(十六进制($1))

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
perl-pi.back-e'y/+/;s/%([\da-f]{2})/chr hex$1/gie./*.log
如果可用,还可以使用
uri\u unescape()
中的
uri::Escape

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
perl-pi.back-MURI::Escape-e'y/+/$\u=uri\u unescape$\u./*.log

带GNU
awk

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
LC_ALL=C gawk-vRS='%[:xdigit:]{2}'
RT{RT=sprintf(“%c”,strtonum(“0x”substr(RT,2)))}
{gsub(/\+/,“”);printf“%s”,“$0 RT}”
将采用标准输入法上编码的URI,并在标准输出法上打印解码后的输出

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

我们将记录分隔符设置为与
%XX
序列匹配的regexp。在GNU
awk
中,匹配它的输入存储在RT特殊变量中。我们从那里提取十六进制数字,将
strnum()
附加到“0x”后变成一个数字,依次传递到
sprintf(“%c”)
在C语言环境中会转换为相应的字节值。

只是想分享另一个解决方案,pure bash:

encoded_string="Mozilla%2F5.0%20%28Macintosh%3B%20U%3B%20Intel%20Mac%20OS%20X%2010.6%3B%20en"
printf -v encoded_string "%b" "${encoded_string//\%/\x}"
echo $encoded_string
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

Python answer的一个稍加修改的版本,它接受一行程序中的输入和输出文件

cat inputfile.txt | python -c "import sys, urllib as ul; print ul.unquote(sys.stdin.read());" > ouputfile.txt
$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }

这里是一个简单的单线解决方案

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
它可能看起来像perl:),但它只是纯bash。没有AWK,没有SED。。。没有间接费用。使用:内置、特殊参数、模式替换和echo内置的-e选项将十六进制代码转换为字符。有关更多详细信息,请参阅bash的手册页。您可以将此函数用作单独的命令

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
$ urldecode https%3A%2F%2Fgoogle.com%2Fsearch%3Fq%3Durldecode%2Bbash
https://google.com/search?q=urldecode+bash
或者在变量赋值中,例如:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
$ x="http%3A%2F%2Fstackoverflow.com%2Fsearch%3Fq%3Durldecode%2Bbash"
$ y=$(urldecode "$x")
$ echo "$y"
http://stackoverflow.com/search?q=urldecode+bash

使用BASH,要从标准输入读取%编码的URL并解码:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
while read; do echo -e ${REPLY//%/\\x}; done
按CTRL-D以发出文件结束(EOF)的信号,然后优雅地退出

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
通过将文件设置为标准文件,可以解码文件的内容:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
while read; do echo -e ${REPLY//%/\\x}; done < file
  • read-build-in命令读取标准输入,直到看到换行符为止。它将一个名为
    REPLY
    的变量设置为它刚刚读取的文本行
  • ${REPLY//%/\\x}
    将“%”的所有实例替换为“\x”
  • echo-e
    \xNN
    解释为十六进制值为
    NN
    的ASCII字符
  • while重复此循环,直到read命令失败,例如达到EOF
上述内容不会将“+”更改为“”。要将“+”也更改为“”,如guest的:

$ function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
  • 是一个BASH内置命令。在这里,它只接受一个参数,不做任何处理
  • 双引号将所有内容放在一个参数内
  • \
    是一个特殊参数,它等于参数展开后上一个命令的最后一个参数。这是
    REPLY
    的值,其中“%”的所有实例都替换为“\x”
  • ${{u//+/}
    将“+”的所有实例替换为