为移动的文件生成新名称以防止覆盖?

为移动的文件生成新名称以防止覆盖?

如果存在同名的现有文件,如何为文件生成新名称?在桌面环境中,通过在文件名末尾附加数字来生成新名称,但是如何从命令行完成此操作?

我正在使用 Android 操作系统和 Busybox。

答案1

假设您有 POSIX shell,您可以执行以下操作:

mv() {
        eval "DEST=\${$#}" #The destination is the last positional parameter
        if [ -e "$DEST" ] && ! [ -d "$DEST" ];then
                PREFIX=${DEST%.*}
                COUNT=1
                EXT=${DEST##*.}
                args= i=1
                while [ $i -lt $# ]; do args="$args \"\${$i}\"" i=$((i+1)); done
                DEST="$NAME-"$(printf "%03d" $COUNT)".$EXT"
                while [ -e "$DEST" ];do
                    COUNT=$((COUNT+1))
                    DEST="$NAME-"$(printf "%03d" $COUNT)".$EXT"
                done
                eval "command mv $args \"\$DEST\""
        else
                command mv "$@"
        fi
}

如何使用这个

这是一个函数,因此请将其保存在您的文件中~/.bashrc并像正常的mv.

这是做什么的

  • 将原始可执行文件的路径存储mvMV变量中
  • 获取调用它的最后一个参数到变量中DEST
  • 如果DEST存在并且不是目录,则此函数假定您的重命名正在尝试破坏文件
  • 然后,它提取最终名称的前缀(最终名称之前的任何内容.,标记扩展名)、扩展名(最终名称之后的任何内容.)、计数(如果有;最终名称之后的前缀中的任何内容-)。
  • 如果未找到计数,则提取的计数将设置为零,否则将设置为上一步中找到的计数。
  • 当前计数增加
  • 然后,该函数使用所有原始参数(开关+文件名)减去最后一个参数来调用自身,并添加新文件名而不是原始调用中的最后一个参数。新名称是旧名称,但在扩展名之前添加了 3 位计数器(零填充)。
  • 该函数是递归的,因为如果你说它mv a.txt b.txt会首先尝试mv a.txt b-001.txt.下一个mv调用也必须是函数本身,因为如果b-001.txt也存在,我们希望继续递增计数器,直到找到不存在的新文件名。
  • 如果最后一个参数不存在或者是一个目录,则mv使用原始参数调用原始可执行文件。

注意事项

  • 您可以重复尝试破坏现有文件的次数取决于计数器的长度(在本例中为 999 次)。您可以选择包含文件系统上 inode 限制的数字,以确保只要您能够创建文件,它就可以工作。
  • 如果您尝试破坏名称类似于 的文件foo-001.txt,它将被移动到foo-001-001.txt.

笔记

  • 要更改命名模式,请将语句3中的 更改printf为您喜欢的任何内容。
  • 这段代码已经测试过
  • 这是非常简单的,我确信在某些边缘情况下它会严重失败。如果您发现任何问题,我很乐意为您尝试修复它们。同时,请勿在生产机器上尝试此操作。

答案2

我通常使用该工具mktemp来创建可靠的临时文件。它默认创建文件,但也可以通过其-d开关创建目录。

例子

以下是如何为当前目录中的文件创建一些临时名称。

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

这将为您创建文件。

参考

答案3

这是 Joseph R 脚本的替代方案,没有任何警告!它将在路径名后附加一个数字后缀(路径可以是目录或文件),递增后缀值,直到找到尚不存在的后缀。其他实用程序例如logrotate使用类似的模式,但旋转所有现有副本,以便新副本始终具有“0”后缀。因为这不是那种意义上的轮换,所以我将其称为dotmv.请记住,这file.0将是最老的复制。

例如:

dotmv somefile.txt

重命名somefile.txt somefile.txt.0,除非后者存在,在这种情况下它将是somefile.txt.1,依此类推。您可以列出多个文件(dotmv this that "the other thing"等等),所有文件都将被点移动。

我相信这是 POSIX 兼容的——它set -o posix在 bash 上运行(但这是一个可疑的测试)。我还用 android (jelly bean 4.2.1) shell 进行了测试,它可以在那里工作。但是,在 Android 上,您需要按照指示更改 shebang 或运行它sh dotmv- 除非您有 root 设备,否则您无论如何都会这样做,因为否则无法使脚本可执行。更改 shebang 将允许您使用exec dotmv.

#!/bin/sh
# On android change that to /system/bin/sh.

# Validate arguments
if [ $# -lt 1 ]; then
    echo "A list of one or more paths is required."
    exit 1
fi

# Checks if a path exists and can be moved.
checkPath () {
    if [ ! -e "$1" ]; then
        echo "'$1' does not exist."
        return 1;
    fi
    if [ ! -w "$1" ]; then
        echo "Cannot move '$1', permission denied."
        return 1;
    fi
    return 0;
}

# Finds a new path with numerical suffix.
getName () {
    suf=0;
    while [ -e "$1.$suf" ]
        do let suf+=1
    done
    Dest=$1.$suf
}

# Loop through arguments -- use quotes to allow spaces in paths.
while (($#)); do
    Src=$1
    Dest=$1
    shift
    checkPath "$Src"
    if [ $? -eq 0 ]; then
        getName "$Src"
        mv "$Src" "$Dest"
    fi
done

希望这里的逻辑非常简单。这可以用 python、C 或任何其他带有文件 I/O 的图灵完备过程语言来实现。

相关内容