将大量数据作为 shell 命令的输入传递

将大量数据作为 shell 命令的输入传递

使用通过管道(非交互式)操作的 bash shell,我尝试将大量数据传递给 shell 命令。到目前为止,我无法让它可靠地工作。

例如,使用此处文档,它看起来像这样:

(sed s/X//|base64 -d|lzcat|tar x) << EOF
XXQAAgAD//////////wAzG+wBunDDREwYD51KYXL50sahXmBTOGSine7WC0RATjpIrem5ygsQWKoZ
XwhPmkJAuCyqnO1KQAoFruXjSOsR3KJY+zHvzYFOgpl3ZJa+1+b0cB0w2vYzj53qplKMTjRkchPnr
XZ/nbloA=
EOF

但对于大量数据,这将不起作用,因为 bash 会在将其传递给命令之前尝试将其全部加载到内存中。

另一方面,如果我在没有此处文档的情况下直接执行此操作,则应将其直接传递给命令,但 shell 似乎会将不可预测的行数解释为 shell 命令:

(sed s/X//|base64 -d|lzcat|tar x)
XXQAAgAD//////////wAzG+wBunDDREwYD51KYXL50sahXmBTOGSine7WC0RATjpIrem5ygsQWKoZ
XwhPmkJAuCyqnO1KQAoFruXjSOsR3KJY+zHvzYFOgpl3ZJa+1+b0cB0w2vYzj53qplKMTjRkchPnr
XZ/nbloA=

我想这与非交互式 shell 缓冲输入的方式有关。

我不需要返回到传递数据的 shell,因此如果其行为可预测,像后一种解决方案对我来说将是可行的。

答案1

拥有千兆字节大小的 shell 脚本的想法对我来说似乎很荒谬。所以只需将数据放入单独的文件中即可。

如果您坚持只使用一个文件:让 shell 忽略此数据。将其放在文件末尾的exit. shell(至少bash)直到文件末尾才读取。

使用外部命令从文件中提取数据并将其传递给预期的命令:

#! /bin/bash

do_something_with_the_data () {
        wc
}

test -f "$0" || exit 3

awk '/^DATABLOCK-1$/ { run=1; next; }; run==0 { next; }; '\
'$0=="" { exit; }; { print; }' "$0" |
        do_something_with_the_data

awk '/^DATABLOCK-2$/ { run=1; next; }; run==0 { next; }; '\
'$0=="" { exit; }; { print; }' "$0" |
        do_something_with_the_data

exit 0

DATABLOCK-1
foo bar baz

DATABLOCK-2
x
y
z

答案2

您正在传递 << EOF,它告诉 shell 在数据中进行扩展和替换。这会让人头疼,并且可能会产生意想不到的影响。您应该引用重定向来禁用数据的 shell 解析,例如: << 'EOF' 但不能引用终止 EOF。如果 EOF 是脚本中的最后一个内容,则允许省略它 (IIRC)。

请量化“海量数据”。我针对客户需求对此进行了测试,但我对 10MB 感到厌倦(那是很久以前在您今天看到的较小的内存上的情况)。

sed 可能是错误的。它仅替换每行中的初始 X。您可能想要: sed 's/X//g'

tar 没有任何可提取的内容。它需要一个存档名称,大概是“-”来读取标准输入。

你的最终版本是错误的。管道根本没有重定向,因此当 sed 在命令行上读取 stdin 时,它将永远挂起。如果以 XXQAAgAD/ 开头,那么它会被解释为命令名称。

我不清楚为什么要在 shell 脚本中嵌入大量静态数据。这就是数据文件和管道的用途。您想在这里解决什么具体问题?

当然,如果您有一个通过 tar 存档、用 xz 压缩、用 base64 编码并通过电子邮件发送给您的文件,那么这一切都非常有意义。除了将数据嵌入到 shell 脚本中的部分之外。以及删除第一个 X 的位。

答案3

如果没有here-doc,它对我来说工作得很好,只要该脚本在标准输入上可用。如果 stdin 是可查找的,Bash 会在运行之前查找到第一行的末尾;如果不是,它一次读取一个字符,将流保留在同一位置。但 Dash(Debian 的 /bin/sh)却没有。

这里的内容是一个 gzip 压缩的 tar 文件,其中有一个名为hello.txt(与问题中的不同)的文件:

$ ls
data.sh
$ cat data.sh 
sed -e 's/^X//' | base64 -d | tar -zxf -
XH4sIANuo0l4AA+3RMQrCQBCF4ak9xZ5AZmc363mCCglEAusoHl8TxM4iRZLm/5rHwCseTHcdhvHo
XL5f16EfJecp4anS+NX1zViSmUnJjOVkUjWbFJOiKm34ed29rCNL7s6/e/u2dL3W8bTFoW930/8Pe
XKwAAAAAAAAAAAAAAAAAAS70BbZqA2QAoAAA=
$ bash < data.sh 
$ cat hello.txt 
hello

也可以看看:

您可能想要使用tar -f -,因为默认输入很可能是磁带驱动器,具体取决于系统(对于 GNU tar,它是如何编译的)。

但实际上,像这样的自解压 shell 脚本会要求用户开始运行您发送给他们的一些代码,这有些可疑。再加上 base-64 编码显着扩展了数据,如果您只是将文件tar作为单独的文件传输,则可以使用更少的空间。也就是说,如果可能的话,我可能应该假设它不是,因为我们正在谈论这样的脚本。

相关内容