【问题标题】:Shell script to recursively copy files with unique filenames to new folders用于递归地将具有唯一文件名的文件复制到新文件夹的 Shell 脚本
【发布时间】:2018-10-01 19:16:22
【问题描述】:

我正在尝试根据文件名将我的文件重组到新目录中。这是通过GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17) OS X High Sierra 完成的

目前我有一个父目录,有 2 组子目录,有 100 多个子目录,然后是一组图像,其文件名在我所拥有的 3000 个左右的文件中很常见。

So Parent(dir) -> Subcategories(dirs) -> Deeper subcategories(dirs) -> Files

文件名都非常相似,即 login.png 或 splash.png,我可能在 3000 个文件中只有 40 个唯一的文件名。然而,子类别是独一无二的。

我正在尝试将所有相同的文件名(即所有 login.png 文件)递归地复制到一个新文件夹中,但使用任意唯一标识符重命名它们。这个唯一标识符无关紧要。它可以是任意数字,甚至可以是父目录名称(即login_subdirectoryname.png

为了获得所有可用文件的列表,我已经尝试了几件事:

所以使用这样的东西至少可以向我展示所有可用的文件:

find ./Parent -name "*.PNG" -type f -exec echo_name.sh basename {} \;

echo_name.sh 很简单:

echo_name(){
    echo $1
}
echo_name $1

我创建了一个函数,因为我认为我们需要执行更多操作,例如检查新目录中存在的名为“login”的文件夹,如果不可用,则创建它。

所以我需要能够在这里完成两件事......

A) 在find 函数中维护一些唯一变量以计数,该函数将传递给echo_name.sh,因此每个文件名只需加一(即login_1.png), 添加父目录名称(即login_thissubcategory. png)以避免在我创建实际的cp 时重复文件名

B) 确定没有扩展名和路径的严格文件名是什么,并确定路径。所以我的函数需要知道:

  • ./Parent/subcategory/deeper_subcategory/login.PNG
  • deeper_subcategory
  • 登录

我知道这是一个复杂的重组,但感谢任何帮助。

【问题讨论】:

  • 我没有看到具体的问题,而且“任何帮助”相当模糊。有什么事情没有按您的预期工作吗?
  • $1 将只是单词basename,而不是文件名。你实际上并没有执行basename 命令。

标签: bash macos shell glob cp


【解决方案1】:

您可以使用 findxargs 与内联 bash 脚本结合使用:

$ mkdir -p parent/category{1,2}/subcat{a,b,c,d,e}/
$ touch parent/category{1,2}/subcat{a,b,c,d,e}/{login,splash}.png
$ find . -type f | nl | xargs -n2 -I@ bash -c 'set -- @; b=${2##*/}; echo cp $2 ${b%.*}_$1.${b##*.}'
cp ./parent/category1/subcata/login.png login_1.png
cp ./parent/category1/subcata/splash.png splash_2.png
cp ./parent/category1/subcatb/login.png login_3.png
cp ./parent/category1/subcatb/splash.png splash_4.png
cp ./parent/category1/subcatc/login.png login_5.png
cp ./parent/category1/subcatc/splash.png splash_6.png
cp ./parent/category1/subcatd/login.png login_7.png
cp ./parent/category1/subcatd/splash.png splash_8.png
cp ./parent/category1/subcate/login.png login_9.png
cp ./parent/category1/subcate/splash.png splash_10.png
cp ./parent/category2/subcata/login.png login_11.png
cp ./parent/category2/subcata/splash.png splash_12.png
cp ./parent/category2/subcatb/login.png login_13.png
cp ./parent/category2/subcatb/splash.png splash_14.png
cp ./parent/category2/subcatc/login.png login_15.png
cp ./parent/category2/subcatc/splash.png splash_16.png
cp ./parent/category2/subcatd/login.png login_17.png
cp ./parent/category2/subcatd/splash.png splash_18.png
cp ./parent/category2/subcate/login.png login_19.png
cp ./parent/category2/subcate/splash.png splash_20.png

前两行只是设置了测试工具。最后一行是有趣的。让我们分解一下:

find . -type f | \ # use find to locate the files -- change this as needed
    nl | \ # number each line -- this gives us the unique id for each
    xargs -n2 -I@ \ # pass the line number and full file name to each...
        bash -c \ # ... instance of bash we start
            'set -- @; b=${2##*/}; echo cp $2 ${b%.*}_$1.${b##*.}'

引号内的肉如下:

  • set -- @ 将两个参数拆分为 $1(第 # 行)和 $2(文件的完整路径)
  • b=${2##*/} 获取文件的基本名称
  • echo cp $2 与 cp 命令的开头相呼应 - 删除 echo 以执行此操作,并根据需要进行调整
  • ${b%.*}_$1.${b##*.} 获取不带扩展名的文件名部分,在末尾添加 _,添加行号,然后添加回 .扩展部分。

我避免处理带有空格、空值、换行符和其他非 ASCII 字符的文件名,因为 OP 建议这不是问题。如果这个问题,则需要额外的技术。

【讨论】: