参考

参考

我打算将我的 btrfs 子卷的完整备份和增量备份传输到磁带存档服务。该服务公开 FTP 和 SSH 端点。如果我被允许在 SSH 端点上执行任意命令,那么我将执行以下操作来执行增量备份:

btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt |
    ssh -p $PORT $USER@$ENDPOINT "cat > $SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg"

然而,我不被允许这么做:

$ ssh -p $PORT $USER@$ENDPOINT
Last login: Mon Jan  3 01:23:45 2067 from 123.456.789.123

This account is restricted by rssh.
Allowed commands: scp sftp rsync 

If you believe this is in error, please contact your system administrator.

Connection to some.remote.endpoint closed.

因此,我考虑使用 SCP 协议进行传输。但是,我的scp二进制文件拒绝传输命名管道:

$ scp -P $PORT <(btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt) \
    $USER@$ENDPOINT:$SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg
/dev/fd/63: not a regular file

讽刺的是,显然,scp曾经做过正确的事情。我想并不是每个人都认为传输命名管道是明智/有用的。 编辑(2017-06-03):这是一个错误的观察。正如 Kenster 所指出的,SCP 协议不允许发送未知大小的文件。

更新(2017-06-04):我还尝试使用 SFTP 协议传输数据。显然,FTP 协议允许发送未知大小的文件,这可以从ftp (关联) 和ncftpput (关联,部分说明,最后一段)二进制文件。我发现我尝试过的 SFTP 客户端没有提供这种支持(sftplftp),这可能表明 SFTP(与 FTP 不同)不支持发送未知大小的文件(与我的想法相反,SFTP 不仅仅是通过 SSH 隧道传输的 FTP;它是一种不同的协议)。

更新(2017-06-05):根据 SFTP 协议第 3 版互联网草案(关联),每个应用级数据包必须在有效载荷之前指定有效载荷长度(关联,第 3 节)。但是,SFTP 支持在写入的文件中搜索(关联,第 6.4 节)明确支持超出当前文件末尾的写入。因此,应该可以在客户端使用一个小的缓冲区,并以已知大小的小块发送未知大小的文件:

#!/bin/bash
# <Exchange SSH_FXP_INIT requests.>
# <Send an SSH_FXP_OPEN request.>
CHUNK_SIZE=32768
OFFSET=0
IFS=''; while read -r -N $CHUNK_SIZE CHUNK; do
  ACTUAL_SIZE=`cat <<<"$CHUNK" | head -c -1 | wc -c`
  # <Send an SSH_FXP_WRITE request with payload $CHUNK of size
  # $ACTUAL_SIZE at offset $OFFSET.>
  OFFSET=$(($OFFSET+$ACTUAL_SIZE))
done < <(command)
# <Send an SSH_FXP_CLOSE request.>

但是,通过 shell 手动进行通信会非常麻烦。我正在寻找一个提供此类功能的 SFTP 客户端。

参考

答案1

lftp4.6.1 和更新版本应该能够做到这一点:https://github.com/lavv17/lftp/issues/104

不幸的是,链接问题中建议的命令不起作用,但稍加修改的命令就可以起作用:

lftp -p $port sftp://$user:$pass@$host -e "put /dev/stdin -o $filename"

由于一个漏洞,如果您使用 SSH 代理,则必须在密码字段中提供一些内容。但任何随机字符串都可以。

此命令从 读取/dev/stdin。如果您必须使用不同的命名管道,我想这也应该可行 - 如果不是,您可以随时cat named-pipe | lftp ...

我已经测试过lftp>100GB 的上传速度,没有任何问题。


rclone还支持 SFTP,并且应该能够使用命令从管道输入上传rcat,将在 1.38(下一个版本)中发布。

答案2

SCP 不太适合您的目的。SCP 协议不支持将未知大小的字节流发送到远程系统以保存为文件。发送文件的 SCP 协议消息要求先发送文件的大小,然后发送组成文件的字节。对于从管道读取的字节流,您通常不知道管道将产生多少字节,因此无法发送包含正确大小的 SCP 协议消息。

(我能找到的关于 SCP 协议的唯一在线描述是这里这里。重点关注“C”字的描述。)

SFTP 协议可用于此类事情。据我所知,普通的sftp命令行实用程序不支持读取管道并将其存储为远程文件。但大多数现代编程语言(perl、python、ruby、C#、Java、C 等)都有 SSH/SFTP 库。如果您知道如何使用其中一种语言,那么编写一个满足您需求的实用程序应该很简单。

如果您无法使用 shell 脚本,则可以伪造 SCP 协议来传输文件。以下是示例:

#!/bin/bash
cmd='cat /etc/group'

size=$($cmd | wc -c)    
{
        echo C0644 $size some-file
        $cmd
        echo -n -e '\000'
} | ssh user@host scp -v -p -t /some/directory

这将在远程系统上创建权限为 644 的文件。文件内容将是写入其标准输出的任何内容some-file。请注意,您将运行该命令两次,无论这意味着资源消耗和副作用如何。并且命令/some/directory$cmd必须每次输出相同数量的字节。

答案3

如果你可以编写 perl,你可以尝试使用网络::SFTP::外国它能够通过 SFTP 从打开的文件描述符传输文件:

#!/usr/bin/perl
# untested!
use Net::SFTP::Foreign;
my $user = ...;
my $host = ...;
my $remote_path = ...;
my $btrfs_cmd = 'btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME';
my $sftp = Net::SFTP::Foreign->new("$user\@$host", ...);
open my($pipe), "$btrfs_cmd | compress | encrypt |"
    or die $!;
$sftp->put($fh, $remote_path);

相关内容