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


8

如果已有同名文件,该如何为该文件生成新名称?在桌面环境中,会在文件名的末尾附加一个数字来生成一个新名称,但是如何从命令行完成呢?

我正在将Busybox与Android操作系统配合使用。


1
您能否扩展Q来指定您要针对android制作这些文件的能力?Java的?您有什么工具,busybox?或者只是香草安卓?
slm

@user slm我将使用它来备份平板电脑上的下载文件夹中包含的文件,该程序根据扩展名将文件排序到相应的文件夹中,我编写了执行此操作的python脚本,但是该程序速度慢,并且如果文件具有相同的名称,它也会覆盖文件。我正在将程序重写为bash,生成新名称是我一直在努力的部分。
kyle k 2013年

谢谢您的回答!然后,您可以使用的版本mktemp吗?
slm

@user slm我已经安装了busybox,它已经包括在内,但是如果我从程序进行系统调用mktemp将不起作用。
kyle k 2013年

不会出现某种错误?另外,您从哪里进行此系统调用?蟒蛇?
slm

Answers:


5

假设您拥有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

这是做什么的

  • 将原始mv可执行文件的路径存储在MV变量中
  • 获取调用它的最后一个参数到变量中 DEST
  • 如果DEST存在并且不是目录,则此函数假定您的重命名试图破坏文件
  • 然后提取最终名称的前缀(在final之前的任何内容.,标记扩展名),扩展名(在final后面的任何内容.),计数(如果有的话;在final后面的前缀中的任何内容-)。
  • 如果未找到计数,则将提取的计数设置为零,否则将其设置为上一步中找到的计数。
  • 当前计数增加
  • 然后,该函数使用所有原始参数(开关+文件名)减去最后一个参数来调用自身,并添加新的文件名,而不是原始调用中的最后一个参数。新名称是旧名称,但在扩展名之前添加了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语句中的更改为所需的内容。
  • 该代码已经过测试
  • 这非常简单,我敢肯定,在某些极端情况下,它会严重失败。如果您发现有问题,我们很乐意为您修复。同时,请勿在生产机器上尝试此操作。

同样的想法在这里:P
Rahul Patil

@RahulPatil除非我尝试复制GUI行为。
约瑟夫·R.

太好了,我还没有使用过GUI。
拉胡尔·帕蒂尔

如果退出,它将覆盖目标文件,我已经测试了paste.ubuntu.com/6071897
Rahul Patil

@RahulPatil不确定您在说什么:在您的示例中,它标识出/tmp一个存在的目录,并称/bin/mv file1 /tmp我看不出问题出在哪里。
约瑟夫R.

3

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

您可以通过以下方法为当前目录中的文件创建一些临时名称。

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

这将为您创建文件。

参考文献


2
mktemp在android上没有,但是无论如何我还是+1,因为这是一个很好的问题的很好的答案,U&L明智的选择。
goldilocks 2013年

@goldilocks-谢谢您的客气话。总是很好!
slm

@goldilocks-如果包括busybox,您知道吗?有一个mktemp的实现,我对android不太了解。code.google.com/p/abb/source/browse/mktemp.c?name=upstream/...
SLM

1
看起来像android上的busybox要求设备已植根(jailbroken)。本机外壳是/system/bin/sh并且似乎是sh兼容的-像if [ -w $file ]工作一样的结构,mv并且rename可用-因此仅使用内置脚本执行此操作的脚本应该在那里工作。
goldilocks 2013年

@goldilocks-感谢您的检查。您在寻找什么来寻找答案?我想用android弄湿我的脚,但是不想拥有电话/设备。
slm

2

这是约瑟夫·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(软糖豆4.2.1)外壳进行了测试,并且在该外壳上可以正常工作。但是,在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的完整的过程语言来实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.