使用通过管道(非交互式)操作的 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
作为单独的文件传输,则可以使用更少的空间。也就是说,如果可能的话,我可能应该假设它不是,因为我们正在谈论这样的脚本。