我需要收集一些重复的文件并希望避免名称冲突。问题是,在文件被清理之前,这个文件集合可能会被我的脚本的另一次执行添加到其中,并且只想继续增加数量。
我决定使用一个简单的 Until 循环来增加数字,如下所示。
until [[ ! -f ${PWD}/DUPES/${num}-$1 ]]; do
num=$((num +1))
done
mv --no-clobber $1 ${PWD}/DUPES/${num}-$1
我无法想象在极端的例子中文件会超过 1k,所以我的问题是......
这是实现这一目标的非常低效的方法吗?我是否应该解析现有文件以获得最高的前导数字以从那里开始递增,或者对于我提出的极端示例来说这可以吗?或者有更好的方法吗?
答案1
GNU mv 有一个--backup
可能有用的选项。以下交互显示了当目标存在时如何重命名文件:
$ touch a b c o
$ mv --backup=numbered --verbose a o
`a' -> `o' (backup: `o.~1~')
$ mv --backup=numbered --verbose b o
`b' -> `o' (backup: `o.~2~')
$ mv --backup=numbered --verbose c o
`c' -> `o' (backup: `o.~3~')
在此示例中,原始文件o
已重命名为o.~1~
、
a
to o.~2~
、b
too.~3~
和c
to o
。因此,这不会以与您发布的代码相同的方式重命名,但这可能是可以接受的,具体取决于您的具体需求。
答案2
你可能会这样做:
set -C; num=0 ### set -o noclobber; init $num
[ -e "$1" ] && ### this needs to be true
until 2>&3 >"./DUPES/$((num+=1))-$1" && ### try to open() num+=1
mv -- "$1" "./DUPES/$num-$1" ### if opened, mv over it
do :; done 3>/dev/null ### do nothing
您将立即确保多个实例无法确保任何给定文件具有相同的名称,并增加您的变量。
< /dev/null
stderr 只是在尝试执行输出截断/重定向并找到现有目标时删除 shell 对存在文件的抱怨。尽管诺克洛伯启用后,它不会覆盖另一个文件 - 它只会覆盖open()
一个新文件,除非您使用>|
.所以你不需要它的抱怨,因为重点是增加现有文件,直到找到不存在的名称。
关于性能方面——它会如果你不是从零开始,就会更好。或者,如果您试图弥补差额。我想上面的内容可能会有所改进,例如:
set -C; num=0 ### set -o noclobber; init $num
until 2>&3 >"./DUPES/$((num+=1))-$1" && ### try to open() num+=1
mv -- "$1" "./DUPES/$num-$1" ### if opened, mv over it
do [ -e "./DUPES/$((num*2))-$1" ] && ### halve fail distance
num=$((num*2)) ### up to a point of course
done 3>/dev/null ### done
...但在 1000 以内您可能不必太担心。我在几秒钟内就获得了 65k 多个随机名称。
顺便说一句 - 你可能认为你可以:
>"./DUPES/$((num+=1))-$1" mv -- "$1" "./DUPES/$num-$1"
...但它在 shell 中不起作用bash
。
num=0; echo >"/tmp/$((num+=1))" >&2 "$num"; echo "$num" /tmp/[01]
0
1 /tmp/1
无论出于何种原因,bash
在其他上下文中进行重定向分配 - 因此扩展以奇怪的顺序发生。因此,您需要一个单独的简单命令来扩展正确的$num
值,正如我使用&&
.否则,尽管如此:
num=0; echo "$((num+=1))" "$num"
1 1
答案3
考虑您的提案的两个实例同时运行、同时启动。他们都会发现(比如说) num=4 可用,并且都会尝试使用相同的目标名称。
我现在无法测试这个替代方案,但这样的东西可能就足够了
num=1 attempt=10
while ! mv --no-clobber "$1" "${PWD}/DUPES/${num}-$1" && [[ 0 -lt $attempt ]]
do
num=$((num+1))
attempt=$((attempt-1))
done
[[ 0 -eq $attempt ]] && echo "ERROR: Too many attempts to handle $1" >&2