【问题标题】:Finding files by type and renaming them based on their parent directory按类型查找文件并根据其父目录重命名它们
【发布时间】:2015-07-22 03:06:50
【问题描述】:

因此,我已尽我所能仔细查看互联网,试图找到一些东西来帮助我解决我目前遇到的问题。

例如,我有一个包含许多目录的文件,其中包含文档和图像。

我的目标是将这些文件重命名为基于它们的父文件夹,例如:

/main/secondary/文件

由于我所有的文件都已经通用命名,我希望能够将我的图像重命名为 secondary0001.jpg secondary0002.jpg 等等。

我一直在寻找并尝试使用各种方法来创建工作脚本。

目前我觉得这可能是我迄今为止的最大努力。

find $2 -type f -iname IMG_[0-9][0-9][0-9][0-9].jpg -exec mv -n {}$dirname {}.jpg\; 

$2 包含我的整个文件夹的文件夹,因此 $2 将等同于 Alpha/Primary/Secondary/file

非常感谢任何形式的帮助,谢谢。

【问题讨论】:

  • 欢迎来到 Stack Overflow。请尽快阅读About 页面。你的问题还不是很清楚。您提到/main/secondary/file,但似乎想在secondary 文件夹中将IMG_0001.jpg 重命名为secondary0001.jpg,因此/main/secondary 部分是目录的示例,但file 部分将映射到图像JPEG 文件。然后你得到了Alpha/Primary/Secondary/file,目前还不清楚你在用它做什么。

标签: shell unix find mv


【解决方案1】:

假设您的图像文件名不包含空格并且您的文件夹名称不包含空格(因此无需极端滑稽动作来处理极其尴尬的文件名),那么您可以考虑:

find "$directory" -type f -iname 'IMG_[0-9][0-9][0-9][0-9].jpg' -print |
while read file
do
    base=$(basename "$file")
    dir=$(dirname "$file")
    bdir=$(basename "$dir")
    suffix=$(echo "$base" | sed 's/^[Ii][Mm][Gg]_//')
    mv "$file" "$dir/$bdir$suffix"
done

我没有说任何关于效率的事情。由于您没有使用 Bash 或 Ksh 对其进行标记,因此我没有假设他们有任何用于变量编辑的工具。除了使用$(…) 代替反引号`…`find-iname 选项外,这基本上适用于过去20 年左右从Bourne shell 派生的任何shell。

如果您决定在目录或文件名中需要空格等,则需要查看代码。它很可能是安全的(因为它在变量引用周围使用双引号,例如"$file"),但您需要真正担心您的文件名或目录名是否可以包含换行符。


使用您的方法,我现在已经找到了重命名这些文件的方法。但是,当我根据它们的目录重命名它们时,我正在覆盖每个文件并丢失许多文件。有没有办法避免这种情况,例如在文件名的末尾添加数字?

  1. 通过在mv 前面加上echo 进行测试,这样您就知道会发生什么,而无需实际发生。
  2. 我认为您必须修改了代码或与合理推断的情况略有不同。下面有一个示例,其中包含一组全新的垃圾目录层次结构中的空文件。每个目录的输入名称都是唯一的;每个目录的输出名称都是唯一的;除非目录中已经存在使用修改后的命名方案的文件,否则脚本无法生成冲突和丢失数据。即使您将文件上移一级,名称也应该是唯一的,因为子目录首先是唯一的。

示例运行:

$ mkdir junk
$ cd junk
$ for dir in primary secondary tertiary
> do (mkdir $dir; cd $dir; touch $(seq -f 'IMG_%04.0f.jpg' 1 10))
> done
$ ls
primary   secondary tertiary
$ ls *
primary:
IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg IMG_0006.jpg IMG_0007.jpg IMG_0008.jpg IMG_0009.jpg IMG_0010.jpg

secondary:
IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg IMG_0006.jpg IMG_0007.jpg IMG_0008.jpg IMG_0009.jpg IMG_0010.jpg

tertiary:
IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg IMG_0006.jpg IMG_0007.jpg IMG_0008.jpg IMG_0009.jpg IMG_0010.jpg
$ directory=.
$ find "$directory" -type f -iname 'IMG_[0-9][0-9][0-9][0-9].jpg' -print |
> while read file
> do
>     base=$(basename "$file")
>     dir=$(dirname "$file")
>     bdir=$(basename "$dir")
>     suffix=$(echo "$base" | sed 's/^[Ii][Mm][Gg]_//')
>     mv "$file" "$dir/$bdir$suffix"
> done
$ ls
primary   secondary tertiary
$ ls *
primary:
primary0001.jpg primary0003.jpg primary0005.jpg primary0007.jpg primary0009.jpg
primary0002.jpg primary0004.jpg primary0006.jpg primary0008.jpg primary0010.jpg

secondary:
secondary0001.jpg secondary0003.jpg secondary0005.jpg secondary0007.jpg secondary0009.jpg
secondary0002.jpg secondary0004.jpg secondary0006.jpg secondary0008.jpg secondary0010.jpg

tertiary:
tertiary0001.jpg tertiary0003.jpg tertiary0005.jpg tertiary0007.jpg tertiary0009.jpg
tertiary0002.jpg tertiary0004.jpg tertiary0006.jpg tertiary0008.jpg tertiary0010.jpg
$

当我在每个目录中创建 1000 个文件并计时移动时,重命名这 3000 个文件需要 46 秒(在带有硬盘且没有 SSD 的 Mac OS X 10.10.4 上运行)。这比我预期的要长一点。

修改脚本如下所示,将每个目录 1000 个文件的运行时间缩短为 8 秒(从 46 秒),速度提高了大约 5 秒。这是一个值得改进的地方,但仍然感觉脚本运行速度没有那么快就像现代 Linux 一样——但这可能是古老机器、硬盘、HTFS 文件系统和 Mac OS X 开销的组合(例如,窗口的标题栏会在脚本运行时更改当前运行的命令名称) .

directory='.'
time find "$directory" -type f -iname 'IMG_[0-9][0-9][0-9][0-9].jpg' -print |
while read file
do
    #base=$(basename "$file")
    base=${file##*/}
    #dir=$(dirname "$file")
    dir=${file%/*}
    #bdir=$(basename "$dir")
    bdir=${dir#*/}
    #suffix=$(echo "$base" | sed 's/^[Ii][Mm][Gg]_//')
    suffix=${base/[Ii][Mm][Gg]_/}
    mv "$file" "$dir/$bdir$suffix"
done

为了进一步改进,我会使用 Perl 并让它作为系统调用执行重命名操作,而不是调用单独的程序。这将减少更多的进程开销(修改后的脚本中仍有 3000 个mv 命令,而 Perl 或等效的整个移动只有一个进程)。

请注意,参数替换是有效的,因为名称被限制为行为良好(每个名称中至少有一个斜线;未命名根目录等)。由basenamedirname 命令处理的边缘情况不会由参数替换处理。谨慎概括。

【讨论】:

  • 对不起,如果我最初的问题不是很清楚,这是关于 bash 的。这对我来说看起来相当流畅和可以理解,我不会在目录或文件名上有任何空格。假设我要处理 1000 张图片,这会被认为是快还是慢?
  • 我预计一千个文件不会超过几秒钟,但我还没有去尝试过。如果这将是一次性操作,那么速度无论如何都不是主要问题。如果您要经常这样做(比如每小时几次),那么您可能会担心它的性能。如果您使用 Bash,您可以使用参数扩展符号 (${…}) 替换对 basenamedirnamesed 的调用。但你这样做并不紧急。
  • 所以使用你的方法我现在已经找到了一种重命名这些文件的方法,但是当我根据它们的目录重命名它们时,我正在覆盖每个文件并丢失很多,有没有办法为了避免这种情况,例如在文件名的末尾添加数字。非常感谢您迄今为止的帮助,这是非常宝贵的。
  • 好的,感谢您进一步解释这一点,我非常感谢您找到了一种更快的方法,除此之外,8 秒一点也不差。所有文件都有通用编号,但是我想知道是否可以为每个文件应用一个新编号。例如:Img_0372.jpg 变为secondary1.jpg Img_2248.jpg 变为secondary2.jpg 值得一提的是,是否还可以限制从目录名复制的字符数量,例如子字符串,例如:Second1.jpg If I'再次让这一点变得足够理解......非常感谢您的持续支持。
  • 如果你能设计一个可靠的可实施方案,它是可以做到的。您负责编辑——您选择名称映射到的内容。当然,您有责任确保新名称是唯一的。您想使用什么算法将 0372 映射到 2248?有很多选择;您要将 0371 和 0373 映射到什么位置?名称的子串属于“你负责”类别;您决定如何修改循环体以将secondary 编辑为second。这取决于您需要代码的通用性。我可能会缩减到 Perl(但我已经使用了 20 多年)。
猜你喜欢
  • 2022-01-06
  • 1970-01-01
  • 1970-01-01
  • 2019-10-07
  • 2015-07-09
  • 2018-04-10
  • 2013-08-22
  • 2012-09-12
  • 2018-02-26
相关资源
最近更新 更多