我已经实现了一个基于 rsync 的系统来将文件从不同的环境移动到其他环境。
我现在面临的问题是,有时候,有同名的文件,但路径和内容不同。
我想让 rsync(如果可能)重命名重复的文件,因为我需要并使用
--no-relative
选项。
重复的文件可以通过两种方式出现:
添加
-b --suffix
选项,允许我对提到的第一个重复文件类型至少有 1 次重复。
一个最小的例子(基于 Linux 的系统):
mkdir sourceDir1 sourceDir2 sourceDir3 destDir;
echo "1" >> sourceDir1/file.txt;
echo "2" >> sourceDir2/file.txt;
echo "3" >> sourceDir3/file.txt;
rsync --no-relative sourceDir1/file.txt destDir
rsync --no-relative -b --suffix="_old" sourceDir2/file.txt sourceDir3/file.txt destDir
有什么办法可以达到我的要求吗?
我不认为你可以直接用
rsync
来做。
这是
bash
中的一个解决方法,它使用 find
和 GNU awk
做一些准备工作,然后调用 rsync
。
想法是按“副本数”对输入文件进行分类(例如,
sourceDir1/file.txt
将是file.txt
的副本#1,
sourceDir2/file.txt
副本#2和sourceDir3/file.txt
副本#3) 并根据 “拷贝数” 生成一个文件,其中包含该类别中所有文件的列表。
rsync
的 --from-file
和每个类别的自定义 --suffix
。
rsync
相比。awk
调用分成两部分来使其更长)。步骤如下:
0) 在您的系统中为
bash
使用正确的 shebang。
#!/usr/bin/env bash
1) 创建用于存储临时文件的目录。
tmpdir=$( mktemp -d ) || exit 1
2) 按“重复数”对输入文件进行分类,为
rsync --from-file
生成文件(每个重复类别一个),并获得类别总数。
read filesCount < <(
find sourceDir* -type f -print0 |
LANG=C gawk -F '/' '
BEGIN {
RS = ORS = "\0"
tmpdir = ARGV[2]
delete ARGV[2]
}
{
id = ++seen[$NF]
if ( ! (id in outFiles) ) {
outFilesCount++
outFiles[id] = tmpdir "/" id
}
print $0 > outFiles[id]
}
END {
printf "%d\n", outFilesCount
}
' - "$tmpdir"
)
3) 为
rsync --suffix
=> 找到一个独特的后缀——使用一组给定的字符生成——字符串应被appended到它。
_old
+数字结尾的现有文件名,则可以跳过此步骤。
(( filesCount > 0 )) && IFS='' read -r -d '' suffix < <(
LANG=C gawk -F '/' '
BEGIN {
RS = ORS = "\0"
charsCount = split( ARGV[2], chars)
delete ARGV[2]
for ( i = 1; i <= 255; i++ )
ord[ sprintf( "%c", i ) ] = i
}
{
l0 = length($NF)
l1 = length(suffix)
if ( substr( $NF, l0 - l1, l1) == suffix ) {
n = ord[ substr( $NF, l0 - l1 - 1, 1 ) ]
suffix = chars[ (n + 1) % charsCount ] suffix
}
}
END {
print suffix
}
' "$tmpdir/1" '0/1/2/3/4/5/6/7/8/9/a/b/c/d/e/f'
)
4) 运行
rsync
(s).
for (( i = filesCount; i > 0; i-- ))
do
fromFile=$tmpdir/$i
rsync --no-R -b --suffix="_old${i}_$suffix" -0 --files-from="$fromFile" ./ destDir/
done
5) 清理临时目录。
rm -rf "$tmpdir"
猜想只有rsync是不可能的。您必须首先列出文件并对其进行分析以解决问题。看看这个命令:
$ rsync --no-implied-dirs --relative --dry-run --verbose sourceDir*/* dst/
sourceDir1/file.txt
sourceDir2/file.txt
sourceDir3/file.txt
sent 167 bytes received 21 bytes 376.00 bytes/sec
total size is 6 speedup is 0.03 (DRY RUN)
让我们用它来创建源文件列表:
mapfile -t list < <(rsync --no-implied-dirs --relative --dry-run --verbose sourceDir*/* dst/)
现在我们可以像这样遍历这个列表:
declare -A count
for item in "${list[@]}"; {
[[ $item =~ ^sent.*bytes/sec$ ]] && break
[[ $item ]] || break
fname=$(basename $item)
echo "$item dst/$fname${count[$fname]}"
((count[$fname]++))
}
sourceDir1/file.txt dst/file.txt
sourceDir2/file.txt dst/file.txt1
sourceDir3/file.txt dst/file.txt2
将 echo 更改为 rsync 即可。