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

Macos 将所有文件从子目录移动到新目录,而不覆盖,macos,bash,random,rename,mv,Macos,Bash,Random,Rename,Mv,我想合并到多个子目录中的1个目录文件中 除了在扩展名之后添加随机字符串外,下面的内容非常接近;我想在分机前得到: find . -type f -iname "[a-z,0-9]*" -exec bash -c 'mv -v "$0" "./$( mktemp "$( basename "$0" ).XXX" )"' '{}' \; 我搜索了几十篇其他帖子,但都没有提到我的具体情况: 我在OSX上(所以它是Bash的BSD风格;例如,mv没有-t选项) 许多文件都有相同的名称,因此我需要在m

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

除了在扩展名之后添加随机字符串外,下面的内容非常接近;我想在分机前得到:

find . -type f -iname "[a-z,0-9]*" -exec bash -c 'mv -v "$0" "./$( mktemp "$( basename "$0" ).XXX" )"' '{}' \;
我搜索了几十篇其他帖子,但都没有提到我的具体情况:

  • 我在OSX上(所以它是Bash的BSD风格;例如,mv没有-t选项)
  • 许多文件都有相同的名称,因此我需要在mv期间重写它们(我不能只为mv使用-n选项,因为太多的文件将无法移动)
  • 这些文件的种类不尽相同,因此我需要使用
    find-typef
  • 我想排除.DS_存储文件,因此一个不错的选择似乎是
    find-typef-iname“[a-z,0-9]*”
  • 我希望重写文件的名称采用:oldname-random_string.xyz的形式(但我也可以将文件重命名为顺序列表:00001.xyz、00002.xyz等)
  • 这些文件从我的主目录向下隐藏了4层:
    • 主/主任
    • 目录2
    • 目录3
    • 目录4
    • 目录5
    • 文件
  • 为了简单起见,与.sh脚本相比,我更喜欢bash命令(但两者我都喜欢)
GNU解决方案 这使用了与您使用的命令基本相同的命令,但我为
mktemp
提供了一个模板,以便
XXX
模式正好出现在后缀之前。使用GNU
sed

find-输入f-iname“[a-z,0-9]*”-exec bash-c'mv-v“$1”。/$(mktemp-u“$(basename“$1”| sed-E-E'\'s/\。([^.]+)$/.XXX.\1/''''-E'\''/'/'./'/'$/.XXX/'''.''''.'''''.'''''''''.'''.''.'''.''/'''.'''''''''''''';
上面的关键添加是使用
sed
在文件名后缀前插入
XXX

sed -E -e 's/\.([^.]+)$/.XXX.\1/' -e '/XXX/ !s/$/.XXX/'
这有两个命令。第一个将
.XXX
放在扩展名之前。仅当文件名没有扩展名时才运行第二个命令,在这种情况下,它会将
.XXX
添加到文件名的末尾

在第一个命令中,源正则表达式由两部分组成。第一个是
\.
,它匹配一个句点。第二个是
([^.]+)$
,它将扩展捕获到组1中。替换将其替换为
.XXX.\1
,其中
\1
是组1的
sed
符号,在本例中,组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
循环将这些文件名逐个读取到变量
fname
fname
包含源文件的完整路径。然后,不带路径的文件名存储在
new
中。然后进行两次检查。如果源文件已在当前目录中,脚本将继续执行下一个循环。类似地,如果文件名id
.DS\u Store
,也将跳过它。(给定的
find
命令已经跳过这些文件。这一行只是为了将来的灵活性。)接下来,文件名分为两部分:
name
ext
,扩展名<代码>分机包括前置期间。接下来,循环检查格式为name.NNN.ext的文件,并在第一个尚不存在的文件处停止。源文件将移动到该名称的文件中

关于GNU解决方案及其兼容性的相关说明
  • 在上面的GNU命令中引用是复杂的。
    bash-c
    的参数需要用单引号引起来,以防止调用bash执行过早的变量替换。此外,bash子shell执行
    sed
    命令时需要使用单引号,以防止历史扩展干扰否定的使用,
    ,在
    sed
    命令中

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

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


感谢您的帮助。但是,这没有帮助:扩展后仍然会出现随机文本。下面是一个示例输出:./0/a/1/IMG_0002.PNG->./IMG_0002.PNG.VRI也:我应该在:bash-c\'mv…中包含“\”吗(两种方式都适用)@natkoy我刚刚将答案复制到我的shell中,并再次尝试,它对我有效。但是,我发现你在OSX上,那里的实用程序经常运行一点。我只是用一个我希望与OSX兼容的版本更新了答案。如果它不起作用,请让我知道确切的错误消息。另外,反斜杠只是为了便于阅读,我将命令写在两行上。为了避免混淆,我将其从更新的答案中删除。再次感谢John。抱歉,这没有做到。我将bash设置为verbose,下面是一个实例的输出:
mv-v“$1”。/$(mktemp-u“$(basename“$1”| sed”s/\.\([^.]\+\)$/.XXX.\1/;/XXX/!s/$/.XXX/”))mktemp-u“$(basename“$1”| sed”s/\.\([^.]\+\)$/.XXX.\1/;/XXX/!s/$/.XXX/”“basename“$1”| sed”s/\.\([^.]\+\)$/.XXX./A/001.jpg->/001.jpg.qzH
下面是运行sed echo时的结果