将所有文件从子目录移动到新目录而不覆盖

时间:2014-05-05 00:20:59

标签: macos bash random rename mv

我想合并到多个子目录中的1个目录文件中。

除了在扩展后添加随机字符串外,以下内容非常接近;我希望它在扩展之前:

find . -type f -iname "[a-z,0-9]*" -exec bash -c 'mv -v "$0" "./$( mktemp "$( basename "$0" ).XXX" )"' '{}' \;

我搜索过其他几十篇文章,但没有解决我的具体情况:

  • 我在OS X上(因此它是Bash的BSD风格;例如,mv没有-t选项)
  • 许多文件都有相同的名称,所以我需要在mv期间重写它们(我不能只为mv使用-n选项,因为太多的文件不会被移动)
  • 文件不是同一类,所以我需要使用find -type f
  • 我想排除.DS_store文件,因此它似乎是一个不错的选择find -type f -iname "[a-z,0-9]*"
  • 我希望重写文件的名称格式为:oldname-random_string.xyz(但我也可以将文件重命名为顺序列表:00001.xyz,00002.xyz等)
  • 文件被隐藏在我的主目录下4层:
    • Master / Top dir
    • Dir 2
    • Dir 3
    • Dir 4
    • Dir 5
    • 文件
  • 为了简单起见,我更喜欢使用bash命令来编写.sh脚本(但我很满意)

1 个答案:

答案 0 :(得分:0)

GNU解决方案

这基本上使用了您使用的相同命令,但我向mktemp提供了一个模板,以便XXX模式出现在后缀之前。使用GNU sed

find . -type f -iname "[a-z,0-9]*" -exec bash -c 'mv -v "$1" "./$(mktemp -u "$(basename "$1" | sed -E -e '\''s/\.([^.]+)$/.XXX.\1/'\'' -e '\''/XXX/ !s/$/.XXX/'\'')" )"' _ '{}' \;

上面的关键添加是使用sed在文件名中的后缀之前插入XXX

sed -E -e 's/\.([^.]+)$/.XXX.\1/' -e '/XXX/ !s/$/.XXX/'

这有两个命令。第一个将.XXX放在扩展名之前。仅当文件名没有扩展名时才运行第二个命令,在这种情况下,它会将.XXX添加到文件名的末尾。

在第一个命令中,源正则表达式由两部分组成。第一个是\.,它匹配一个句号。第二个是([^.]+)$,它将扩展名捕获到第1组。替换替换为.XXX.\1,其中\1sed表示第1组,在我们的例子中是文件的扩展名。

OSX解决方案

在OSX下,mktemp没有用,因为它只支持XXX部分跟踪的模板。作为解决方法,我们可以使用生成非重叠文件名的bash脚本:

#!/bin/bash
find . -type f -iname "[a-z,0-9]*" -print0 |
while IFS= read -r -d '' fname
do
    new=$(basename "$fname")
    [ "$fname" = "./$new" ] && continue
    [ "$new" = .DS_store ] && continue
    name=${new%.*}
    ext=${new#"$name"}
    n=0
    new=$(printf '%s.%03i%s' "$name" "$n" "$ext")
    while [ -f "$new" ]
    do
        n=$(($n + 1))
        new=$(printf '%s.%03i%s' "$name" "$n" "$ext")
    done
    mv -v "$fname" "$new"
done

以上使用find命令获取文件名。选项-print0用于确保它适用于困难的文件名。 while循环将这些文件名逐个读入变量fnamefname包含源文件的完整路径。然后,没有路径的文件名存储在new中。然后执行两次检查。如果源文件已在当前目录中,则脚本将继续执行下一个循环。同样,如果文件名为.DS_Store,则也会跳过它。 (给定的find命令已经跳过这些文件。这一行只是为了将来的灵活性。)接下来,文件名分为两部分:nameext ,扩展名。 ext包括领先时期。接下来,循环检查格式为name.NNN.ext的文件,并在第一个尚未存在的文件处停止。源文件将移动到该名称的文件。

关于GNU解决方案及其兼容性的相关说明

  • 在上面的GNU命令中引用很复杂。 bash -c的参数需要使用单引号来防止调用bash执行过早变量替换。此外,当由bash子shell执行时,sed命令需要在单引号中,以防止历史扩展干扰!命令中的否定sed的使用。

  • OSX(BSD)sed不支持将命令与分号组合在一起。因此,每个命令都通过单独的sed选项提供给-e

  • OSX(BSD)sed似乎与{GNU +区别对待sed。使用-E(扩展正则表达式)选项时,这种不兼容性似乎消失了。 (相应的GNU选项为-r但是,作为未记录的兼容性功能,GNU sed也支持-E