我以前用过 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"