【发布时间】:2014-05-08 22:35:30
【问题描述】:
我正在使用 scp 命令将文件从一台 Linux 主机复制到另一台。 我在主机 1 上运行 scp 推荐并将文件从主机 1 复制到主机 2。文件很大,复制它需要一些时间。 复制开始后,host2 上的文件立即出现。即使复制仍在进行中,我也可以使用此文件执行所有操作。
有没有可靠的方法来确定主机2上的复制是否完成?
【问题讨论】:
我正在使用 scp 命令将文件从一台 Linux 主机复制到另一台。 我在主机 1 上运行 scp 推荐并将文件从主机 1 复制到主机 2。文件很大,复制它需要一些时间。 复制开始后,host2 上的文件立即出现。即使复制仍在进行中,我也可以使用此文件执行所有操作。
有没有可靠的方法来确定主机2上的复制是否完成?
【问题讨论】:
如果您使用inotify-tools,
那么解决方案将如下所示:
while ! inotifywait -e close $(dirname ${bigfile_fullname}) 2>/dev/null | \
grep -Eo "CLOSE $(basename ${bigfile_fullname})$">/dev/null
do true
done
echo "File ${bigfile_fullname} closed"
【讨论】:
如果避免第二次 SSH 握手很重要,您可以使用以下内容:
ssh host cat \> bigfile \&\& touch complete < bigfile
然后等待在远程端创建“完整”文件。
【讨论】:
经过一些调查,以及在其他论坛上对该问题的讨论,我找到了另一种解决方案。也许它可以帮助某人。
有一个命令“lsof”。它列出了打开的文件。复制过程中会打开文件,所以命令
lsof | grep filename
将返回非空结果。
因此,您可能需要创建一个while 循环以等待 lsof 什么都不返回并继续执行您的任务。
示例:
# provide your file name here
f=<nameOfYourFile>
lsofresult=`lsof | grep $f | wc -l`
while [ $lsofresult != 0 ]; do
echo still copying file $f...
sleep 5
lsofresult=`lsof | grep $f | wc -l`
done; echo copying file $f is finished: `ls $f`
【讨论】:
lsof 将查询所有进程以查找打开的文件。为什么不直接使用fuser检查文件? fuser file 将打印打开了file 的PID,如果进程是ssh,那么它仍在被复制。既不能依赖lsof 也不能依赖fuser,因为即使被中断,ssh 也会使文件保持原样。唯一可靠的方法是在文件关闭后获取校验和。在您的问题中,您说的是“可靠的方式”。
本地和远程文件的校验和(md5sum、sha256sum、sha512sum)会告诉您它们是否相同。
对于您无法通过 SSH 访问远程系统(例如 FTP 服务器)的情况,您可以在文件上传后下载文件并比较校验和。我为工作中从生产脚本发送的文件执行此操作。下面是我执行此操作的脚本中的 sn-p。
MD5SRC=$(md5sum $LOCALFILE | cut -c 1-32)
MD5TESTFILE=$(mktemp -p /ramdisk)
curl \
-o $MD5TESTFILE \
-sS \
-u $FTPUSER:$FTPPASS \
ftp://$FTPHOST/$REMOTEFILE
MD5DST=$(md5sum $MD5TESTFILE | cut -c 1-32)
if [ "$MD5SRC" == "$MD5DST" ]
then
echo "+Local and Remote files match!"
else
echo "-Local and Remote files don't match"
fi
【讨论】:
对于重复的问题,How to check if file has been scp 100% to the remote location,这是一个期望脚本,要知道文件是否完全传输,我们可以添加期望 100% .. .. 即类似这样的......
expect -c "
set timeout 1
spawn scp user@$REMOTE_IP:/tmp/my.file user@$HOST_IP:/home/.
expect yes/no { send yes\r ; exp_continue }
expect password: { send $SCP_PASSWORD\r }
expect 100%
sleep 1
exit
"
if [ -f "/home/my.file" ]; then
echo "Success"
fi
【讨论】:
在发送端(host1)使用这样的脚本:
#!/bin/bash
echo 'starting transfer'
scp FILE USER@DST_SERVER:DST_PATH
OUT=$?
if [ $OUT = 0 ]; then
echo 'transfer successful'
touch successful
scp successful USER@DST_SERVER:DST_PATH
else
echo 'transfer faild'
fi
在接收方(host2)制作这样的脚本:
#!/bin/bash
SLEEP_TIME=30
MAX_CNT=10
CNT=0
while [[ ! -e successful && $CNT < $MAX_CNT ]]; do
((CNT++))
sleep($SLEEP_TIME);
done;
if [[ -e successful ]]; then
echo 'successful'
rm successful
# do somethning with FILE
fi
使用CNT 和MAX_CNT 可以禁用无限循环(以防文件successful 未传输)。
产品MAX_CNT 和SLEEP_TIME 应该等于或大于预期的传输时间。在我的示例中,预期传输时间小于 300 秒。
【讨论】:
在我的脑海中,您可以执行以下操作:
touch tinyfile
scp bigfile tinyfile user@host:
那么当tinyfile出现时你就知道bigfile的传输完成了。
正如 cmets 中所指出的,这假定 scp 将按照指定的顺序一个一个地复制文件。如果您不信任它,您可以明确地一一进行:
scp bigfile user@host:
scp tinyfile user@host:
这种方法的缺点是您可能需要进行两次身份验证。如果这是一个问题,您可以使用 ssh-agent 之类的内容。
【讨论】:
scp 决定一次可以复制多个文件。