rsync 重命名 dest 目录中的重复文件

问题描述 投票:0回答:2

我已经实现了一个基于 rsync 的系统来将文件从不同的环境移动到其他环境。

我现在面临的问题是,有时候,有同名的文件,但路径和内容不同。

我想让 rsync(如果可能)重命名重复的文件,因为我需要并使用

--no-relative
选项。

重复的文件可以通过两种方式出现:

  1. dest目录下已有同名文件
  2. 在同一个 rsync 执行中,我们正在不同位置传输具有相同名称的文件。例如:dir1/file.txt 和 dir2/file.txt

添加

-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

有什么办法可以达到我的要求吗?

bash ubuntu rsync file-transfer
2个回答
0
投票

我不认为你可以直接用

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
    相比。
  • 安全:它永远不会覆盖文件(参见下面的步骤#3)。
  • 健壮:处理任何文件名,即使其中有换行符。
缺点
  • 目标目录必须为空(否则它可能会覆盖一些文件)。
  • 代码有点长(我通过使用一些 process substitutions 并将
    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"

0
投票

猜想只有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 即可。

© www.soinside.com 2019 - 2024. All rights reserved.