LINUX Bash - 递归创建中间目录并将文件类型移动到新目录中

LINUX Bash - 递归创建中间目录并将文件类型移动到新目录中

我以前用过 StackExchange,也潜伏在 SuperUser 上,但这是第一次发帖。以下是我想做的事情。(伪)

For Each DIR containing $file *.Item
    Mkdir ./dir1/dir2
    Mv $file ./dir1/dir2/$file

基本上,我有一个充满 .Item 类型文件的文件夹结构,在每个包含 .Item 类型文件的目录中,我想创建 2 个新的子目录,分别称为 dir1/dir2/,然后我想将所有 .Item 类型的文件移动到这些子目录中。

到目前为止,我很幸运地完成了部分工作,但未能一次性完成全部工作。如果这个问题已经得到解答,我深表歉意。

答案1

这里的主要问题是避免多次移动文件。一个简单的循环可能会找到dir/x.Item,将其移动到dir/dir1/dir2/x.Item,然后找到dir/dir1/dir2/x.Item并将其移动到dir/dir1/dir2/dir1/dir2/x.Item,然后……

尽管我无法find在几分钟的测试中让 Gnu 做到这一点,但我确实注意到 Posix 规定“如果从正在搜索的目录层次结构中删除或添加文件,则不确定是否find在搜索中包含该文件。”所以应该采取防御措施。

因此,最简单的安全解决方案是先构建文件列表,然后处理该列表。

如果没有.Item文件名称中包含换行符,则可以轻松使用它find来构建列表。假设文件不是太多,可以直接将其放入 bash 数组中:

mapfile -t files < <(find . -name '*.Item')

然后就可以直接处理文件了:

for f in "${files[@]}"; do
  # make the target directory if it doesn't yet exist (-p)
  mkdir -p "$(dirname "$f")"/dir1/dir2
  # move the file
  mv "$f" "$(dirname "$f")/dir1/dir2
done

如果文件很多,我们需要提高效率,不要太过浪费内存。我们可以构造一个目录列表,而不是文件列表(sort -u消除重复):

mapfile -t dirs < <(find . -name '*.Item' -printf %h | sort -u)

现在我们可以处理目录:

for d in "${dirs[@]}"; do
  mkdir -p "$d/dir1/dir2"
  mv "$d/*.Item" "$d/dir1/dir2"
done

最后,如果有很多目录,我们可以使用临时文件而不是数组:

tmp=$(mktemp)
find . -name '*.Item' -printf %h | sort -u > "$tmp"
while IFS= read -r dir; do
  mkdir -p "$d/dir1/dir2"
  mv "$d/*.Item" "$d/dir1/dir2"
done < "$tmp"
rm "$tmp"

相关内容