我需要根据经过的时间处理一组文件(因为我无法控制何时将新文件添加到远程目录)。
我必须对这组文件执行多项操作,因此我尝试将find
结果存储在变量中(这样我可以确保经过的候选者不会更改),然后find
对每个操作使用相同的列表(无需再次执行)。
这是我正在尝试的精简版本。
FILELIST=$(ssh ${SSHUSER}@${SSHHOST} "cd ${REMOTEPATH}; find -amin +${TIME} -type f,d -print")
if [[ ${#FILELIST} -lt 4 ]]; then
echo "No files" >> ${LOGFILE}
exit 1
fi
echo -en "Copying remote files to remote backup\n" >> ${LOGFILE}
ssh ${SSHUSER}@${SSHHOST} "cd ${REMOTEPATH}; cp -Rpv '${FILELIST}' ${REMOTEBACKUPPATH}" >> ${LOGFILE}
这当然给了我cp: missing destination file operand after ''
我所理解的。我只是不知道如何以cp
喜欢的方式格式化文件列表。
我已经为此工作了一段时间,阅读文档/SE 帖子,但无法弄清楚。我希望一旦我知道自己做错了什么,cp
我也可以将这些知识应用到rsync
.
我是 bash 脚本领域的新手,如果这还不清楚的话。 :)
提前致谢。
答案1
bash
不是处理这个问题的最佳外壳。我建议zsh
改为(您已经zsh
通过不引用某些参数扩展来使用语法):
filelist=(
${(0)"$(
ssh $SSHUSER@$SSHHOST "
cd -- ${(qq)REMOTEPATH} &&
find . -amin +${TIME} -type f,d -prune -print0"
)"}
)
if (( $#filelist < 4 )); then
print No files >> $LOGFILE
else
print "Copying remote files to remote backup" >> $LOGFILE
print -rNC1 -- $filelist | ssh $SSHUSER@$SSHHOST "
cd -- ${(qq)REMOTEPATH} && xargs -r0 cp -Rpvt ${(qq)REMOTEBACKUPPATH}"
fi
(假设$SSHHOST
是一个 GNU 系统(你自己已经使用了一些 GNUisms,比如f,d
,没有文件传递到find
, cp -v
),并且那里的登录 shell$SSHUSER
是类似 Bourne 的)。
我已-prune
在find
命令中添加了内容,因为您将以任何方式复制这些整个目录,因此无论如何都没有必要在其中查找更多文件。读取这些目录的内容也会更新它们的上次访问时间。
无论如何,请注意,读取目录中的文件不会更新目录的访问时间。当读取目录自身的内容时,目录的访问时间会更新,就像您在目录中使用ls
/find
或 shell 全局变量时一样(另请注意,如今,访问时间并不总是得到彻底维护)。所以不清楚你到底想做什么。
上面我们使用:
filelist=( values )
定义一个大批变量(其中filelist=value
定义一个标量变量代替)。${(0)"$(cmd)"}
cmd
分割NUL 上的输出。使用 NUL 分隔列表是传递文件路径列表的安全方法,因为 NUL 是文件路径中唯一不允许的字符。cd -- ${(qq)REMOTEPATH}
, with--
来防止$REMOTEPATH
以 开头的值-
(不是-
它本身,但在某些 shell 中也不是-4
/+3
),以及引用的值$REMOTEPATH
(在此处由本地 shell 扩展)(使用单引号与qq
,唯一安全的方法$REMOTEPATH
)对于远程 shell(假设它是类似 Bourne 的,如果登录 shell 是csh/fish/rc... ,则对于某些值不起作用$SSHUSER
)。- 请注意,
&&
它确保仅在cd
成功时才运行下一个命令,否则您将在错误的目录中运行它。 -print0
:打印 NUL 分隔的文件路径(见上文)。- 然后
$#filelist
将包含匹配文件的数量(我们将其与原始代码中的 4 个进行比较,不知道为什么)。 - 然后我们需要将该列表发送回远程主机。我们不是将其作为为远程 shell 发送的 shell 代码中的参数传递(为此我们会遇到各种问题和限制),而是在 stdin
print -N
上将其以 NUL 分隔(用 )传递ssh
,这是继承的在xargs
那儿。 xargs
读取该列表并将其转换为参数cp -t target
(-t
也是 GNU 扩展)。xargs
将根据需要运行尽可能多的cp
s 以避免参数大小限制。