我需要仅使用 BusyBox 1.35 中提供的和/或其他工具来单向同步本地文件夹,cp
该工具没有rsync
.我希望得到类似: 的东西cp -auv /source/ /target
,但它不会删除目标上已消除的项目。目标最终必须是源的精确副本,但它已经拥有其中的大部分,因此只需进行一些更新覆盖、新添加和搬迁可能需要,没有任何交互式提示。
更新。如果BusyBox没有cp -auv /source/ /target
已经完成了单向同步所需的所有操作除了删除目标上的旧内容,是否有任何命令/脚本来“比较”两个文件夹并删除目标上仍然存在的项目?也许类似的东西 diff -qr /source /target | xargs rm -rf
,会起作用吗?diff
答案1
很久以前,我已经在 busybox 1.22 下成功使用了以下脚本。它快速而肮脏地模拟非常基本 rsync
功能(仅基于时间和大小的同步+日志记录),你当然应该先回顾一下并可能增强它以处理文件删除(rsync --del
如功能)
还要注意您可以轻松摆脱的最终交互部分。 :
#!/bin/sh
echo "#############################################"
echo "SHELL : $SHELL"
echo "BASH : $BASH"
echo "TERM : $TERM"
DATENOW=`date +%Y%m%d-%H%M%S`
# last argument
LASTARG=`echo "$@" | awk ' { print $NF } '`
# penultimate argument
num_args=$#
num_args_max=$num_args
let num_args=$num_args-1
PENUARG=` echo "$@" | awk -v vk=$num_args ' { print $vk } ' `
TRG="$LASTARG"
echo "#############################################"
ProcedureStartCopyOneFile() {
echo "*** COPY ***"
echo "File not exists : $FILETRG" >> ~/.config/bashmv/bashmv.log
echo -en "\r Copying ($i) (...)"
echo "$i" | cpio -pvdmu "$TRG"
echo -en "\r Copied ($i)."
TRSS=` du -hs "$FILETRG" --apparent-size | cut -f 1 `
SRSS=` du -hs "$i" --apparent-size | cut -f 1 `
if [ ! -d "$i" ] && [ "$SRSS" != "$TRSS" ] ; then
echo "Different size: $FILETRG"
echo "** WARNING **"
echo "Different size: $FILETRG" >> ~/.config/bashmv/bashmv.log
echo "** WARNING **" >> ~/.config/bashmv/bashmv.log
exit
else
echo -en "\r Copied ($i) [OK]"
fi
echo "*** COPY OK ***"
echo ".*.*"
}
ProcedureCopying() {
echo "> Parameters: Source: $SRC => Target: $TRG"
if [ -f "$SRC" ] || [ -d "$SRC" ] ; then
echo "Source: $SRC"
else
echo "Directory or file source $SRC not found."
exit
fi
if [ "$TRG" = "" ] ; then
exit
fi
if [ "$BASH" != "/bin/bash" ] ; then
echo "Warning: You should use BASH : /bin/bash !"
exit
else
echo "Intpreter BASH: $BASH [OK] "
fi
echo "** START ** "
[ ! -d ~/.config/bashmv ] && mkdir -p ~/.config/bashmv
[ ! -d "$TRG" ] && mkdir -p "$TRG"
if [ "$LASTARG" = "--debug" ] ; then
find "$SRC" -print
exit
fi
find "$SRC" -print | grep -v "^$\|^#" | while read -r i ; do
# echo "Processing $i"
FILETRG="${TRG}/${i}"
DATENOW=`date +%Y%m%d-%H%M%S`
echo "> Start: .*.* ($i)"
if [ ! -f "$FILETRG" ] && [ ! -d "$i" ] ; then
echo ".*.*"
echo "*** COPY ***"
echo "File not exists : $FILETRG" >> ~/.config/bashmv/bashmv.log
echo "$i" | cpio -pvdmu "$TRG"
TRSS=` du -hs "$FILETRG" --apparent-size | cut -f 1 `
SRSS=` du -hs "$i" --apparent-size | cut -f 1 `
if [ ! -d "$i" ] && [ "$SRSS" != "$TRSS" ] ; then
echo "Different size: $FILETRG"
echo "** WARNING **"
echo "Different size: $FILETRG" >> ~/.config/bashmv/bashmv.log
echo "** WARNING **" >> ~/.config/bashmv/bashmv.log
exit
else
echo "Copied ($i) [OK]"
fi
echo "*** COPY OK ***"
echo ".*.*"
elif [ -d "$i" ] ; then
mkdir -p "$FILETRG"
echo "Creating $FILETRG"
elif [ ! -d "$i" ] ; then
TRSS=` du -hs "$FILETRG" --apparent-size | cut -f 1 `
SRSS=` du -hs "$i" --apparent-size | cut -f 1 `
# echo "Source: $SRSS vs $TRSS"
if [ -d "$i" ] ; then
echo "Directory."
fi
if [ ! -d "$i" ] ; then
if [ -f "$FILETRG" ] && [ "$SRSS" = "$TRSS" ] ; then
echo "file $FILETRG exists. [Same size]"
fi
if [ "$SRSS111kkk" = "$TRSS" ] ; then
echo "Different size: $FILETRG"
echo "** WARNING **"
echo "Different size: $FILETRG" >> ~/.config/bashmv/bashmv.log
echo "** WARNING **" >> ~/.config/bashmv/bashmv.log
FILETRGERROR="${TRG}/Error/${i}"
[ ! -d "$TRG/Error" ] && mkdir -p "$TRG/Error"
echo "$TRSS" | cpio -pvdmu "$TRG/Error/${DATENOW}-${i}"
fi
fi
fi
done
echo "Finished!"
if [ "$LASTARG" = "--rm" ] ; then
echo "Delete the directory: $SRC [y/n] ? "
read inpud
[ "$inpud" = "n" ] && exit
rm -rf "$SRC"
fi
exit
全部归功于一些匿名且有用的作者。
答案2
为了找到答案,需要最后的删除步骤、审查、批评。首先我们更新目标。我们假设目标上的项目不能比源更新,即源是目标的唯一更新代理。我们仍然需要删除从源头消除的旧项目,但cp
不这样做。我们将使用 @ceving 的 grep 命令,如下所示https://stackoverflow.com/a/51761017/612313
cp -auv /source/. /target
grep -v -F -x -f <(find /source -type f -printf '%P\n') <(find /target -type f -printf '%P\n')
这将打印 /target 的所有子文件夹中所有过时文件相对于 /target/ 的路径,但不打印子文件夹本身。现在,我只需要学习如何将此输出提供给rm /target/$1
以及如何查找和删除空子文件夹作为最后一步。
也许是这样的(一句台词太长了):
for i in $(grep -v -F -x -f <(find /source -type f -printf '%P\n') <(find /target -type f -printf '%P\n')); do echo /target/$i; done
或者,
grep -v -F -x -f <(find /source -type f -printf '%P\n') <(find /target -type f -printf '%P\n') | while read i; do echo /target/$i; done
甚至更短(?),因为我们可以将整个输出发送到echo
(rm
),但必须首先更改工作文件夹,因为我们将无法再将 /target/ 附加到每一行
cd /target
echo $(grep -v -F -x -f <(find /source -type f -printf '%P\n') <(find . -type f -printf '%P\n'))
到目前为止我最好的候选解决方案:
mkdir /target
pushd /target
cp -auv /source/. .
rm -v $(grep -v -F -x -f <(find /source -type f -printf '%P\n') <(find . -type f -printf '%P\n'))
find . -empty -type d -delete
popd
(除非任何由于某种原因在目标上较新的文件都不会被触及;如果此脚本是目标的唯一更新程序,则有效)
待测试里面的疑问initrd
:我们处于哪个shell中?如果是 Ash 之类的东西,那么pushd/pod
可能无法使用,$()
扩展包也无法使用。将报告我的发现...