split
因此,我正在尝试使用如下方法获取大型块设备的内容:
split --bytes 10M --numeric-suffixes --filter='cat | ssh root@$remote_ip "gzip >> /root/myfilecopy.gz"' /dev/myblockdev
但是,当我将远程文件名保存在环境变量中并在 中使用该变量split
时filter
,它不起作用:
remote_file=/root/myfilecopy.gz
split --bytes 10M --numeric-suffixes --filter='cat | ssh root@$remote_ip "gzip >> $remote_file"' /dev/myblockdev
这是我得到的:
bash: -c: line 0: syntax error near unexpected token `newline'
bash: -c: line 0: `gzip >> '
split: with FILE=x00, exit 1 from command: cat | ssh [email protected] "gzip >> $remote_file"
看起来环境变量在过滤命令中没有正确扩展。
有任何线索知道如何修复此问题吗?
谢谢。
答案1
太复杂了?
暂时把变量放在一边。这是你的基本代码:
split --bytes 10M --numeric-suffixes --filter='cat | ssh root@$remote_ip "gzip >> /root/myfilecopy.gz"' /dev/myblockdev
首先:cat
在这里没用。
接下来,我认为将连续的 -s 的结果附加gzip
到同一个文件 ( >>
) 会取消 的工作split
。理论上,最后你得到的只是 gzip 压缩的/dev/myblockdev
,就像你所做的一样:
ssh root@$remote_ip 'gzip > /root/myfilecopy.gz' < /dev/myblockdev
实际上,我会考虑竞争条件。我确实希望在本地按顺序split
运行-s;但如果在某些情况下,远程端的各种缓冲区或滞后导致下一个开始写入,而前一个完成,我也不会感到惊讶。这会损坏。只打开文件一次可以避免这种情况。ssh
gzip
myfilecopy.gz
如果您使用$FILE
(请参阅man 1 split
)并写入多个文件,我会看到使用的意义split
。请注意,这不会引入竞争条件,因为每个文件都只会打开一次。
结论:split
可能没用,cat
肯定没用;粗心的附加可能会损坏生成的文件。
您询问的问题
好的,让我们假设您有理由使用这种split
方式>>
(cat
尽管您的方式仍然没用)。
您当前的 shell 知道 的值$remote_file
,但split
及其过滤器是子进程,除非您export
事先指定,否则它们不会继承该变量。不存在的变量将扩展为空,因此相关片段看起来像gzip >> (newline)
,因此出现错误。
同样适用于$remote_ip
。我猜你的代码中你没有使用$remote_ip
实际的 IP 地址192.168.0.105
。从现在起,我remote_ip
使用占位符(即不是 shell 变量)表示实际的 IP 地址。
到export
:
remote_file="/root/myfilecopy.gz"
export remote_file
# now your split command should utilize the variable as you expected
$remote_file
或者,您可以在运行 时扩展当前 shell split
。最初,$remote_file
字符串保持不变,因为它被单引号括起来。以下语法仅针对这一个变量更改引号的类型:
split --bytes 10M --numeric-suffixes --filter='ssh root@remote_ip "gzip >> '"$remote_file"'"' /dev/myblockdev
# close a single quote ^ ^ and open again
# variable inside double quotes ^^^^^^^^^^^^^^
这种方式split
永远不会得到字面意思$remote_file
,而是得到它的价值。
另一个问题
如果您有remote_file="/root/myfile copy.gz"
,路径中的空格会将其拆分为远程端的两个参数。因此,更强大的方法需要额外的引号。这是上述带有额外引号(用 转义\
)的“无导出”方法:
split --bytes 10M --numeric-suffixes --filter='ssh root@remote_ip "gzip >> \"'"$remote_file"'\""' /dev/myblockdev
让我们一步一步地剥离它。除其他参数外,split
将以下内容视为一个参数:
--filter=ssh root@remote_ip "gzip >> \"/root/myfile copy.gz\""
它将在以下位置运行此过滤器bash
:
ssh root@remote_ip "gzip >> \"/root/myfile copy.gz\""
然后ssh
就会看到这是它的一个论点:
gzip >> "/root/myfile copy.gz"
因此在远程端,重定向将是"/root/myfile copy.gz"
。如果没有添加这些引号,您将得到:
gzip >> /root/myfile copy.gz
相当于
gzip copy.gz >> /root/myfile
补充笔记
- 如果本地 CPU 足够快,请考虑
gzip
之前ssh
。这样,您可以通过网络链接推送更少的数据;特别是如果您准备好块设备以进行良好的压缩。 - 如果您的目的是运行多个
gzip
进程以充分利用多个 CPU 核心,那么请注意,它们可能还是会按顺序运行;如果不是,则会出现竞争条件 — — 您不希望出现这种情况。熟悉pigz
。