Linux shell:去除换行符的 Base64 解码
Posted
技术标签:
【中文标题】Linux shell:去除换行符的 Base64 解码【英文标题】:Linux shell: Base64 Decode with removing line breaks 【发布时间】:2018-11-02 03:51:36 【问题描述】:我有一个文件,其中每一行都是 base64 编码的 XML 文档。解码的 XML 文档可能包含换行符。我想找出每个包含给定单词的 XML 文档。
问题是,当我对文件的行进行解码时,每个 base64 编码的行都有多行,我无法再对其进行 grep。我需要像base64 decode + remove line breaks
这样的东西一步。
如何在 Linux shell 中实现这一点?我有 Python、Perl 和 awk 可用。
>cat fileContainingBase64EncodedXMLsInEachLine.txt | what should I write here?
输入:
PGZvbz4NCjxiYXIvPg0KPC9mb28+
PGZvbz4NCjxodWh1Lz4NCjwvZm9vPg==
PGZvbz4NCjxiYXJvbWV0ZXIvPg0KPC9mb28+
预期输出
假设我想要包含“bar”的 XML 文档
<foo>
<bar/>
</foo>
<foo>
<barometer/>
</foo>
我的问题的一个例子
>cat fileContainingBase64EncodedXMLsInEachLine.txt | base64 --decode | grep bar
交付:
<bar/>
<barometer/>
所以我没有包含bar
和barometer
的完整xml 文档。
【问题讨论】:
您的描述不清楚。在您的帖子中添加更多输入和输出示例,并让我们也知道您在这方面的努力。 我希望现在说清楚。如果没有请反馈。谢谢。 是什么阻止您搜索多行字符串? @Gábor:我不明白为什么您认为嵌入的换行符会阻止您搜索要查找的关键字。能否请您展示您为解码 base64 数据而编写的代码? 顺便说一句,我不会尝试直接在 shell 中执行此操作。使用 Bash 可以逐行解析文件,但效率不高。最好用 Python、awk 或 perl 编写一个小脚本。向我们展示一些代码,以及几行实际的输入和输出,解释它做错了什么,我们可以帮助您修复它。 【参考方案1】:这是一些 Python 代码,它接受文件名后跟命令行上的搜索词。像往常一样,如果任一 arg 包含空格,则必须用引号引起来。
import sys
from base64 import b64decode
fname, pattern = sys.argv[1:]
with open(fname) as f:
for row in f:
row = b64decode(row).decode()
if pattern in row:
print(row, end='\n\n')
使用“bar”作为模式 arg 对您的数据运行此操作:
<foo>
<bar/>
</foo>
<foo>
<barometer/>
</foo>
为了练习我相当生疏的 awk 技能,我决定编写一个 awk 命令行来执行此操作。它使用标准的base64
命令进行解码。
awk 'BEGINcmd="base64 -d"; print |& cmd; close(cmd,"to"); z=""; while(cmd |& getline s) z=z s "\n"; close(cmd); if (z~pat)print z' pat='bar' testdata_b64.txt
您可以使用pat
参数将模式传递给它,该参数可以是正则表达式。您可以通过标准输入向其发送数据,也可以在命令行中为其指定一个或多个文件名。
请注意,正则表达式模式需要双重转义,例如 pat='\\<bar\\>'
匹配单词 bar
。
【讨论】:
肯定比我下面的答案好得多。 @ZaphoOxx +1 自我批评 :) 问题:如果可以使用标准输入,它会是什么样子? @GáborLipták 是的,如果您愿意,您可以将sys.stdin
硬编码为文件名。但是,在 Linux 上,您可以使用 /dev/stdin
将标准输入作为文件名传递。
我很想知道 awk 版本的速度与我的 python 版本相比如何。 awk 在简单的文本处理方面速度更快,但在 base64
命令的管道中有一些开销。【参考方案2】:
更新:如果你知道第一个节点名称是<foo>
,那么你可以这样做:
$ echo "<head>$(base -decode <file>)</head>" | \
xmlstarlet sel -t -m '//bar/ancestor::foo' -c .
它选择名为bar
的节点的祖先foo
,因为foo
是第一个xml节点,它会选择请求的xml文件。
原始答案如下:
使用xmlstarlet
你可能想要这样做
$ echo "<head>$(base -decode <file>)</head>" | \
xmlstarlet sel -t -m '//bar/ancestor::*[last()-1]' -c .
这实际上选择了节点“bar”的祖先的完整 xml-tree,但它只会到达正确的深度。
我添加了一个额外的head
节点以使完整的字符串成为有效的xml
文件。这样你只需要从第一个节点开始打印。
echo
会产生类似(略有不同的版本):
<head>
<foo />
<foo>
<barometer />
</foo>
<foo>
<DDD>
<BBB/>
<bar />
</DDD>
</foo>
</head>
xmlstarlet
将根据 xpath //bar/ancestor::*
进行模板选择,导致以下匹配集
<bar />
<DDD><BBB /><bar /></DDD>
<foo><DDD><BBB /><bar /></DDD></foo>
<head> everything </ head>
我们对倒数第二个感兴趣,即[last()-1]
,我们要求打印一份-c .
【讨论】:
【参考方案3】:Perl 的救援:
perl -MMIME::Base64 -nE '$_=decode_base64($_);/bar/&&say' fileContaining...txt
或
cat fileContaining...txt | perl -MMIME::Base64 -nE'$_=decode_base64($_);/bar/&&say'
【讨论】:
【参考方案4】:您可以尝试以下 python 脚本。它不是在线命令行工具,但这应该可以满足您的需求。用法如下:
>python3 get_xml.py SEARCHSTRING FILENAME
你的例子的输出是:
<foo>
<bar/>
</foo>
<foo>
<barometer/>
</foo>
脚本:
import base64
import sys
script_name = sys.argv[0]
search_string = sys.argv[1]
filename = sys.argv[2]
print("[+] () search for ".format(script_name,search_string,filename))
with open(filename,"r") as xml_in:
nextline = xml_in.readline()
while nextline != '':
xml = base64.b64decode(nextline).decode("utf-8").rstrip()
if search_string in xml:
print(xml)
nextline = xml_in.readline()
【讨论】:
【参考方案5】:您可以在循环中使用tr
来删除每个 XML 文档的所有新行,如下所示:
#!/bin/bash
while IFS='' read -r line
do
echo -n "$line" | base64 --decode | tr -d '\r\n'
echo
done < fileContainingBase64EncodedXMLsInEachLine.txt
【讨论】:
请参阅Why is using a shell loop to process text considered bad practice?。主要原因是read
占用大量 CPU 资源:它会针对它读取的每个字符向内核发出系统调用。以上是关于Linux shell:去除换行符的 Base64 解码的主要内容,如果未能解决你的问题,请参考以下文章
Java Base64加码解码 Base64.encodeBase64 ( ) 和 new BASE64Enccoder( ).encode( )区别