我有以下文件结构:
raw:
F1:
file1.pdf
F2:
file2.pdf
(...)
pdf:
(...)
每个文件夹始终只有一个 pdf 文件,并且文件/文件夹名称可以是任意的。
我想将raw
目录中的所有pdf文件复制到pdf目录中,并将其文件名替换为其根文件夹名称。对于上面的例子,结果如下:
raw:
F1:
file1.pdf
F2:
file2.pdf
(...)
pdf:
F1.pdf
F2.pdf
(...)
我已经阅读过有关通配符复制的内容,但这显然是一个问题,因为我必须使用两个嵌套的通配符来复制我的文件。所以这显然行不通:
cp raw/*/*.pdf pdf/*.pdf
有没有办法解决这个问题,或者我是否必须循环我的文件夹并使用正则表达式来分割路径?
答案1
您可以使用通配符进行复制,这样就可以了。但是,您需要重命名文件,而不仅仅是复制它们,这意味着您必须为每个文件分配一个新名称,这意味着循环是不可避免的。您最多可以使用一个为您执行循环的工具。
如果您有perl-rename
(renamed
在基于 Debian 的系统上调用),您可以执行以下操作:
$ rename -n 's|raw/([^/]+)/.*pdf|pdf/$1.pdf|' raw/*/*.pdf
raw/F1/file1.pdf -> pdf/F1.pdf
raw/F2/file2.pdf -> pdf/F2.pdf
.pdf
这将找到的任何一级子目录中以 结尾的所有文件名raw
,将它们重命名为其父文件夹的名称加上.pdf
扩展名,并将它们保存在pdf/
.这-n
意味着“只打印您将要执行的操作,而不是执行此操作”,因此,如果您对此感到满意,请运行该命令,而无需-n
实际重命名文件:
rename 's|raw/([^/]+)/.*pdf|pdf/$1.pdf|' raw/*/*.pdf
或者,您可以使用 shell 循环:
for f in raw/*/*pdf; do
new=$(sed -E 's|raw/([^/]+)/.*|pdf/\1.pdf|' <<<"$f");
echo "mv -- $f $new";
done
mv raw/F1/file1.pdf pdf/F1.pdf
mv raw/F2/file2.pdf pdf/F2.pdf
同样,如果您对它的工作感到满意,请删除回显以实际重命名文件:
for f in raw/*/*pdf; do
new=$(sed -E 's|raw/([^/]+)/.*|pdf/\1.pdf|' <<<"$f");
mv -- "$f" "$new";
done
重要的:两种解决方案都假设每个目录只有一个文件,因此不会发生冲突。他们将删除您拥有的任何额外文件,因为同一子目录中的所有文件将获得相同的名称。
您可以尝试使用-i
每个相应工具的选项(mv
an rename
),这将使它们在覆盖同名文件之前要求您确认。这将使解决方案:
rename -i 's|raw/([^/]+)/.*pdf|pdf/$1.pdf|' raw/*/*.pdf
和
for f in raw/*/*pdf; do
new=$(sed -E 's|raw/([^/]+)/.*|pdf/\1.pdf|' <<<"$f");
mv -i -- "$f" "$new";
done
答案2
使用嵌套循环:
shopt -s nullglob # To prevent failures if there's no pdf in a dir.
for dir in raw/* ; do
for pdf in "$dir"/*.pdf ; do
cp -- "$pdf" pdf/"${dir#raw/}".pdf # Remove the "raw/" part
done
done
或迭代文件并应用参数扩展两次以生成目标名称:
for pdf in raw/*/*.pdf ; do
target=${pdf#raw/} # Remove the "raw/" part
target=${target%/*} # Remove the filename
cp -- "$pdf" pdf/"$target".pdf
done
答案3
和zsh
:
autoload -Uz zmv # best in ~/.zshrc
zmv -C 'raw/(*)/*.pdf' 'pdf/$1.pdf'