将文件中列出的图像文件从多个子目录复制到一个公共目录

将文件中列出的图像文件从多个子目录复制到一个公共目录

我有大约 8 个子目录,其中包含图像文件。我需要将大约 6000 张照片中的 300 张从这些子目录移动到一个公共目录。我有一个文件,其中包含我需要移动的文件的名称(而不是路径)。我该怎么做?

我见过类似的方法,但没有一种可以读取文件中的名称,在子目录中搜索该名称以获取路径,然后将该文件移动到公共目录,然后转到下一个目录。我找不到让 find 读取文件的方法。这将在 MacOS 计算机上进行,但任何使用 Python 或 bash 的程序都应该可以正常工作。我不擅长编码,尤其是多变量编码。我以为我可以 cat 文件,然后以某种方式将其发送给 find,然后 find 可以将该目录路径发送给 copy 命令,但 find 命令对我来说是致命的。

谢谢任何有效的帮助。

答案1

在这种情况下,find这不是最好的工具。经过一番努力,可以编写一个命令,从文件中读取并构建一个find命令,用于检查多个目录中的多个名称;或多个find命令:每个名称一个命令,甚至每个名称和目录的笛卡尔积成员一个命令。

仍然测试类似-name-path-regex(如果支持)模式。我明白您想提供准确的名称。这些名称可能很安全,但也可能不安全。例如,Awesome.Picture.*OMG*.jpg如果将名称解释为文件名模式或正则表达式,则名称可以匹配不止这个精确的字符串。通常,您需要清理每个名称,以便在解释后它仅与原始字符串匹配。

在 shell 中使用精确名称很容易。如果有 300 个名称和大约 8 个目录需要检查,它们的笛卡尔积包含大约 2400 对。即使是速度较慢的 shell 也可以在合理的时间内检查它们。与实际复制所需的时间相比,这可能微不足道。

以下是我的假设:

  1. 带有名称的文件每行指定一个文件名。
  2. 文件中的每一行都以不属于相应文件名的换行符结尾。请注意,此假设也适用于最后一行(比较这个答案)。
  3. 文件中指定的每一行都应按字面意思理解。空格(如果有)属于文件名;引号、反斜杠等也一样。
  4. 您想要检查几个精确的目录,而不是它们的子目录(即不进行递归搜索)。

这应该可以做到:

#!/bin/sh -

target="$1//."
shift

while IFS= read -r name; do
   for dir do
      source="$dir/$name"
      test -f "$source" && </dev/tty cp -i -- "$source" "$target" && break
   done
done

将其另存为contraption并使其可执行(chmod +x contraption)。使用方式如下:

</file/with/names ./contraption /target/dir /source/dir1 /source/dir2 /another/source

如果需要,请指定更多源目录。

笔记:

  • 我本意是编写可移植的代码。我认为代码是可移植的,尽管通常可能需要调整shebang,因为POSIX不强制sh/bin/sh;也不强制env/usr/bin/env(所以#!/usr/bin/env sh理论上不是更好;在实践中参见这个答案)。
  • IFS= read -r name解释如下:理解IFS= read -r line
  • 由于test -f代码不会(尝试)复制匹配文件这不是常规文件。
  • </dev/tty cp -i是为了保护目标目录中已经存在的文件。标准cp -i只会破坏一些东西(因为它会从文件的标准输入中读取)。注意/dev/tty由 POSIX 指定
  • --解释如下:--(双破折号)是什么意思?.sh -解释如下:为什么会-发生#! /bin/sh -这种事?
  • 最后一个斜杠$target(添加在target="$1//.")确保它是一个目录。这里之所以有点,只是因为我希望我的代码非常安全(请参阅这个答案,它解释了两者)。如果您已经用尾部斜杠指定了第一个位置参数,那应该没关系。如果您指定了,我会注入两个斜杠(而不是一个)以获得///.(不是) 。重点是//.///. 可能会很麻烦。请注意,//.如果您指定一个空字符串作为目标,您将得到(解决方案不是指定空字符串,它不是有效的路径名)。
  • break如果复制成功,则脚本将转到下一个名称,而不检查尚未检查的目录中的当前名称。如果您希望目录包含具有相同名称的文件,请在命令行中先指定首选源目录,然后再指定次要源目录。

相关内容