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

所以我没有包含barbarometer 的完整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='\\&lt;bar\\&gt;' 匹配单词 bar

【讨论】:

肯定比我下面的答案好得多。 @ZaphoOxx +1 自我批评 :) 问题:如果可以使用标准输入,它会是什么样子? @GáborLipták 是的,如果您愿意,您可以将 sys.stdin 硬编码为文件名。但是,在 Linux 上,您可以使用 /dev/stdin 将标准输入作为文件名传递。 我很想知道 awk 版本的速度与我的 python 版本相比如何。 awk 在简单的文本处理方面速度更快,但在 base64 命令的管道中有一些开销。【参考方案2】:

更新:如果你知道第一个节点名称是&lt;foo&gt;,那么你可以这样做:

$ 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::* 进行模板选择,导致以下匹配集

&lt;bar /&gt; &lt;DDD&gt;&lt;BBB /&gt;&lt;bar /&gt;&lt;/DDD&gt; &lt;foo&gt;&lt;DDD&gt;&lt;BBB /&gt;&lt;bar /&gt;&lt;/DDD&gt;&lt;/foo&gt; &lt;head&gt; everything &lt;/ head&gt;

我们对倒数第二个感兴趣,即[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 解码的主要内容,如果未能解决你的问题,请参考以下文章

BASE64 官方方法,我自己用的,注意记住换行问题。

Java Base64编码存在 情况

Java Base64加码解码 Base64.encodeBase64 ( ) 和 new BASE64Enccoder( ).encode( )区别

shell去除换行和空格2

Linux shell 示例

安装conda后终端出现的(base)字样去除方法