在写这篇文章之前,我显然搜索了互联网并找到了几个可能可行的解决方案。但我对命令行编程或写作完全是新手。
我所拥有的:数千个图像文件存储在具有多个子目录的父目录中。
我想要做的是:使用带逗号分隔符的 CSV 文件重命名所有这些文件(包括子目录中的文件)。典型的行如下所示:
IMG_20221207_094623.jpg,photo_1.jpg
IMG_20221207_094627.jpg,photo_2.jpg
IMG_20221207_094651.jpg,photo_3.jpg
我在这个论坛上找到了一篇描述使用 sed 函数的命令行的帖子。
对于那里提出的几乎相同的问题(除了子目录部分),@terdon 提出了以下命令行
sed 's/"//g' files.csv | while IFS=, read orig new; do mv "$orig" "$new"; done
@terdon 提供的解决方案对于当前目录中的文件效果很好,但无法处理子目录中的文件,除非我还没弄清楚如何操作。
命令行返回的内容如下:
mv:无法统计‘20221207_094623.jpg’:没有此文件或目录
如果我尝试在 csv 文件的 origin 列中添加文件的完整路径,则会返回此信息
mv:无法统计'/home/antoine.delauney/Images/00-Visite/20221207_094623.jpg':没有该文件或目录
对于 {'s/"//g'} 命令的第一部分,我的文件没有引号,因此我不需要像此序列一样删除它们。但是,如果没有命令行失败,我无法编辑第一个序列。如果有人可以指导我完成第一部分,那就太好了,因为我有时必须处理单个目录中的批量图像文件。
但是,这篇文章的核心问题是 sed 命令(直接或可能与附加命令一起)是否能够通过探索给定目标的所有子目录来递归处理一批文件?这将非常有帮助!
先感谢您。
附言:如果我的英语并不总是使用正确的术语,我真的很抱歉。我是法国人,我会说一点英语,但为了写这篇文章,我极大地帮助了谷歌翻译。因此可能会有不恰当或未使用的术语(尤其是命令行错误的例子)。这是我的歉意。
答案1
从父目录中,您可以执行以下操作:
while IFS=, read -r orig new; do find -type f -name "$orig" -execdir echo mv -n -- {} "$new" \; ; done < files.csv
echo
用于试运行...将其删除以进行实际重命名。
上面的单行代码与下面这个多行代码相同:
while IFS=, read -r orig new; do
find -type f -name "$orig" -execdir echo mv -n -- {} "$new" \;
done < files.csv
解释:
在 shell 中(IEbash
), 你可以使用 bash 的内置命令逐行读取文件read
命令以及其选项-r
保存并传递反斜杠,即\
到循环体,而不是将它们解释为转义符并立即在循环头部使用,这在处理文件名时非常重要...可以使用 shell 的输入重定向<
例如像这样:
while IFS=, read -r orig new; do
# Some command/s here
done < file
根据文件中的数据结构,每一行将由 shell 的单词拆分根据内部字段分隔符的值将其分为字段IFS
,并且您可能需要将其从默认值更改为数据结构中使用的实际字段分隔符/定界符,例如,IFS=,
在您的情况下将拆分字段,
而不是默认的空格/制表符/(新队)... shell 将分配第一个(从左边) 字段为第一个传递的参数,orig
即第二个(如果字段数多于传递的参数总数,则为其余字段) 字段到第二个字段,即new
...因此,循环将以两个变量开始$orig
,并$new
连续保存文件第一行中的两个字段,即原始文件名(例如)IMG_20221207_094623.jpg
和新文件名(例如photo_1.jpg
...然后这些变量在循环体内可用,随时可以扩展和使用...在循环体内,以下命令:
find -type f -name "$orig" -execdir mv -n -- {} "$new" \;
将翻译为:
find -type f -name "IMG_20221207_094623.jpg" -execdir mv -n -- {} "photo_1.jpg" \;
那是 ...find
将在当前目录及其所有子目录中搜索-type f
(仅限文件) 和-name "IMG_20221207_094623.jpg"
(精确文件名区分大小写)然后将搜索结果传递给find
的操作-execdir
(mv
这将从包含匹配文件的子目录运行命令 ie)... 因此将当前工作目录中任何名为的文件重命名IMG_20221207_094623.jpg
为(photo_1.jpg
这是默认的搜索路径find
,你也可以根据需要指定它,.
例如find . -type f ...
) 及其所有子目录... 是s 输出/结果{}
的占位符,用于find
指定应按参数顺序放置和处理的位置...请注意,需要来指示s 参数\;
的结束...还要注意, s 选项是一个很好的安全措施,因为它将防止覆盖具有相同文件名的现有文件,并将指示选项的结束,这样就不会因以破折号开头的文件名而阻塞...在循环体中的所有命令之后(-execdir
mv
-n
--
mv
-
即两个命令分组结构之间do ... done
)执行,然后执行循环头(IEwhile ....
) 将执行从文件中读取下一行的操作,并重复相同的过程,直到读取文件中的所有行,然后退出循环。
注意:
你可以告诉mv
打印它正在做的事情(当它修改文件时)通过使用选项,-v
如下所示:
find -type f -name "$orig" -execdir mv -v -n -- {} "$new" \;
您可以将输出重定向到日志文件,例如files.log
:
find -type f -name "$orig" -execdir mv -v -n -- {} "$new" >> files.log \;
相关信息: