自动递增文件名

自动递增文件名

我需要收集一些重复的文件并希望避免名称冲突。问题是,在文件被清理之前,这个文件集合可能会被我的脚本的另一次执行添加到其中,并且只想继续增加数量。

我决定使用一个简单的 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~ato o.~2~btoo.~3~cto 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/nullstderr 只是在尝试执行输出截断/重定向并找到现有目标时删除 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

相关内容