我想使用scp上传文件,但有时目标目录可能不存在。

是否可以自动创建文件夹?如果是这样,怎么办?如果没有,我可以尝试其他替代方法吗?

#1 楼

这是rsync可以做的许多事情之一。

如果您使用的是过去几年发行的rsync版本,¹其基本命令语法类似于scp:²

$ rsync -r local-dir remote-machine:path


local-source及其内容复制到远程计算机上的$HOME/path/local-dir,创建所需的目录。³

rsync确实有一些限制,即可能会影响这在您的特定情况下是否有效。例如,它不会创建多个级别的远程目录丢失。它只会在遥控器上最多创建一个丢失的级别。您可以通过在rsync命令之前添加类似以下内容来轻松解决此问题:

$ ssh remote-host 'mkdir -p foo/bar/qux'


如果不存在,将创建$HOME/foo/bar/qux树。如果它已经存在,它不会抱怨或做任何其他坏事。

rsync有时还会有其他令人惊讶的行为。基本上,您是在要求它弄清楚您要复制的内容,其猜测可能与您的假设不符。试试看。如果它的行为不符合您的预期,并且您看不到原因,请发布有关本地和远程目录设置的更多详细信息,并提供您尝试过的命令。


脚注:


rsync 2.6.0(2004年1月1日)之前,它需要-e ssh标志使其表现得像scp,因为它默认为过时的RSH协议。
scprsync分享一些标志,但是只有一点点重叠。
当使用SSH作为传输协议时,rsync使用相同的默认值。因此,就像scp一样,默认情况下,它将假定在远程计算机上存在与本地用户同名的用户。


评论


我正在使用Mac OSX,是否需要在本地和远程都安装rsync?

– AGamePlayer
15年3月30日在13:27

搜索了rsync的文档页面,未找到如何使用ssh-key登录。我不想使用用户名/密码登录

– AGamePlayer
2015年3月30日13:28



@AwQiruiGuo:Mac OS X附带了rsync。是的,您也确实需要在远程端进行rsync,就像使用scp一样。至于SSH密钥,rsync实际上是在默认的-e ssh下面使用SSH,因此,如果您设置了与scp一起使用的密钥,它们也将对rsync起作用。

–沃伦·杨(Warren Young)
15年3月30日在13:29

@WarrenYoung -r没有区别。您描述的方案仅在复制目录并且目标父目录存在的情况下才有效。因此,rsync foo / u @ h:〜/将创建目标目录foo,而rsync foo / u @ h:〜/ bar /将不创建目标目录bar。只有在bar /存在的情况下,才会创建bar / foo。无论如何,OP都在上传文件,即使在这种情况下,它也仅适用于整个目录。

– terdon♦
15年3月30日在13:29

是的,我也一直在玩。似乎指定文件名也会使其失败。因此,如果foo /不存在,则rsync文件host:/ foo / file失败,但是rsync文件host:/ foo /创建目录并将文件放置在其中。奇怪。无论如何,您说的没错,基本方案可行,我将收回我的不赞成票。

– terdon♦
15年3月30日在13:55

#2 楼

如果要复制一组文件,则不是。如果要复制目录及其下的所有内容,则可以。给定以下命令:如果directory~/the/target上的host中不存在~the/target,则会创建它。但是,如果scp不存在,则您可能会遇到问题-我不记得这种情况的确切结果,但要做好准备使预期的q4312079q失败。

评论


scp:/ home / madmike / test_bin:没有这样的文件或目录。一旦缺少目标目录,这会破坏我的设置。在客户端上使用cygwin,在服务器上使用RHEL 6.9。你的设置是什么?

–MadMike
18-2-12在15:00



#3 楼

据我所知,scp本身无法做到这一点,不。但是,您可以仅将ssh移至目标计算机,创建目录,然后进行复制。
ssh user@host "mkdir -p /target/path/" &&
    scp /path/to/source user@host:/target/path/

请注意,如果要复制整个目录,则不需要上述内容。例如,要将目录~/foo复制到远程主机,可以使用-r(递归)标志:
scp -r ~/foo/ user@host:~/

将在远程主机上创建目标目录~/foo。但是,它无法创建父目录。除非目标目录scp ~/foo user@host:~/bar/foo存在,否则bar将失败。无论如何,如果要复制单个文件,则-r标志将无助于创建目标目录。

评论


声明:“例如,要将目录〜/ foo复制到远程主机,可以使用-r(递归)标志:”,但是,下面的示例不使用-r标志/ o​​ption / argument / parameter (随便说一下):scp〜/ foo / user @ host:〜/。就是说,您的答案仍然获得18票赞成+1 :)

–WinEunuuchs2Unix
20年6月28日在18:59

@ WinEunuuchs2Unix感谢,已修复。

– terdon♦
20年6月29日在8:10

#4 楼

在通过btrfs传输ssh快照之前,我使用以下函数:

check_remote_dir() {
        printf "\ntesting remote directory: '' "

        if ssh -p $PORT $ROOT@$REMOTE "[ ! -d  ]"; then
                printf "\nCreating:  on $ROOT@$REMOTE\n"
                ssh -p $PORT $ROOT@$REMOTE "mkdir -p "
        else
                printf "[OK]\n"
        fi
}


只需在脚本中调用以下函数:

check_remote_dir /my/remote/path

评论


我做了几乎相同的事情,但是我不得不手动输入密码(出于安全原因),所以我最终至少要输入两次...

– Stefan Reich
18/12/14在12:25

#5 楼

是的你可以。根据scp的手册页:


man scp

.....

-r递归地复制整个目录。请注意,scp遵循在树遍历中遇到的symâ
bolic链接。

....


评论


那将不会创建目标父目录。它可以将目录复制到现有位置。因此,如果远程bar /存在,则scp -r foo / u @ h:〜/ bar将创建bar / foo,但如果不存在则将失败。

– terdon♦
2015年3月30日13:32

它不会失败。它将foo内容复制到新目录/ bar。当然,名称已更改,这可能不是OP想要的。如果您想要的是在目标计算机中创建的新树结构,那么scp不会执行此任务,如果您想要从树中的某个分支复制树结构,则将创建该结构。

– Yomismo
2015年3月30日13:46



是的,但这仅在复制目录而不是文件时有效。 scp -r文件主机:〜/ bar /不会创建bar /。

– terdon♦
15 Mar 30 '15 at 13:57

#6 楼

当仅限于没有ssh的scp或sftp(由rssh使用)时,可以通过使用rsync复制空目录树然后复制文件来创建目录。

这就是我放置公共ssh密钥的方式当ssh-copy-id不起作用并且.ssh目录不存在时,远程主机:

rsync -e 'ssh -p22' -av -f"+ */" -f"- *" ~/.ssh backup@1.2.3.4:~/
scp -P22 ~/.ssh/id_rsa.pub backup@1.2.3.4:~/.ssh/authorized_keys


可以将此命令组合到一个rsync中,再添加一个如果目标文件名相同,则在中间包含过滤器。

#7 楼

您可以将命令与mkdir命令链接在一起,然后引用您创建的文件夹:

mkdir ~/new-folder/ && scp -P 22 <remote/url>:~/new-folder/


评论


(1)问题说“自动”。这似乎不是自动的。 (2)这似乎只是对terdon答案的细微差别。

– G-Man说“恢复莫妮卡”
19年1月22日,9:50

#8 楼

那么“ smart” scp + ssh mkdir怎么样?
这是一个脚本,用于每个目录仅创建一次目标目录层次结构(而不​​是每次都盲目地ssh mkdir -p)。
Rationale
我正在复制运动记录(视频文件)写入本地磁盘(事件驱动)时。


rsync没有意义,因为进行完整目录比较没有意义-我正在转移每个事件一个文件。如果我将rsync限制为仅一个子目录或文件,那么它将无法创建完整的目标目录层次结构。

scp可以很好地工作,但是它不知道目标目录是否存在,而且我不想为每个文件运行ssh mkdir -p。

解决方案

第一次需要通过ssh创建每个目标目录(第一次我们将要复制到其中。)
将目标目录名缓存在一个数组中,这样我们就不会在每次看到它时都不会再次调用它。每天清除此数组,以免它变大。
将文件保存起来。

示例代码:
 ssh mkdir 

典型输出,说明“智能” mkdir行为:
 #!/bin/bash

TARGETHOST="some.hostname"
TARGETPATH="/data/backups/MOTION/"

ensure_dir() {
    if [ "$TODAY" != "$(date +%D)" ] ; then
        CREATED=()  # reset array daily and first time so it won't get huge
        TODAY=$(date +%D)
    fi  
    if [[ ! " ${CREATED[@]} " =~ "  " ]]; then
        # create dir
        echo "Creating remote directory: "
        ssh $TARGETHOST "mkdir -p ${TARGETPATH}"
        CREATED+=("")
    fi  
}

# This line is specific to my use case, watching a directory for new files:
inotifywait -mr --format '%w %f' -e close_write --exclude '.jpg' /data/MOTION/ | while read filedir filename ; do
    RELDIR=$(echo $filedir | sed 's|/data/MOTION/||')
    ensure_dir "$RELDIR"
    echo "Copying '$filename' from '${filedir}' to '${RELDIR}'"
    scp -r -p "${filedir}${filename}" "${TARGETHOST}:${TARGETPATH}${RELDIR}${filename}"
done