我有数千张照片,文件名中包含出生年份。我需要为每个出生年份查找并复制至少 100 个文件,比如说 2000 年出生的 100 个文件,2001 年出生的 100 个文件,...,依此类推。
这是文件名的格式:
35077502_1995-02-01_2012.jpg
我猜这张照片是2012年拍摄的。
可以使用bash脚本来完成吗?
谢谢
答案1
#!/bin/bash
IFS=$'\n' years=( $(find . -maxdepth 1 -name '*.jpg' -print0 |
sed -zEn 's/^.*_([0-9][0-9][0-9][0-9])-.*\.jpg/\1/p' |
tr '\0' '\n' |
sort -u)
)
for year in "${years[@]}" ; do
mkdir -p "$year"
find . -iname "*_${year}-*.jpg" -size +1k -print0 |
head -z -n 100 |
xargs -0r cp -t "$year"
done
这将构造一个数组 ( $years
),其中包含从当前目录中的文件名中提取的唯一 4 位年份集合,其中年份前面是下划线 ( _
),后面是破折号 ( -
)。这需要akased
选项的 GNU 版本。-z
--null-data
对于每一年,它首先为该年创建一个目录(如果该目录尚不存在),然后用于find
列出与所需模式匹配且大小大于 1 KB 的所有文件名。然后通过该列表head
仅获取前 100 行,然后将xargs
文件复制到适当的目录。
文件名列表在整个管道中以 NUL 结尾,以便它适用于所有有效的文件名(即,如果文件名中包含空格、制表符、换行符或其他不寻常但完全有效的字符,它不会中断)
这也需要GNU 版本head
(这是 Linux 上的标准),因为它使用-z
选项(也 --zero-terminated
称为 NUL 终止输入)。具体来说,它需要一个版本更新于 2016 年 1 月 13 日。它还需要 GNU 的(又名)cp
选项,该选项允许目标目录作为第一个参数而不是最后一个。-t
--target-directory
如果需要对文件进行排序,则可以在和命令sort -z
之间插入- 例如.这也需要 GNU 版本的.find
head
find ... -print0 | sort -z ... | head -z ...
sort
这假设,如您问题的修订中所示,文件名有一个下划线,后跟年份作为.jpg
扩展名之前的最后一个内容。
如果年份可以出现在文件名中的任何位置,您可能需要使用-iname "*${year}*.jpg"
(不带下划线,并且在和*
之间有一个秒),但要注意开头的八位数字类似于 的文件,其中包含子串。${year}
.jpg
60420017
2001
这还假设您的所有文件都有(不区分大小写).jpg
扩展名(而不是.jpeg
, .jpe
, .jfif
, .gif
,.png
等)。如果需要多个文件扩展名,-iregex
可以使用该选项而不是-iname
.
答案2
如果文件名中没有令人讨厌的东西,你可以做
for year in 2000 2001; do
cp `ls *${year}*.jpg|head -n 100` destination
done
答案3
和zsh
:
for y ({1995..2017}) (cp -- **/*_$y.jpg(.LK+1[1,100]) destination)
**/
:在任何级别的子目录中,按字母顺序排序.
:仅限常规文件LK+1
:长度超过1KiB[1,100]
: 前一百个。
(由于排序顺序将决定复制哪些文件,因此您可能需要添加n
glob 限定符以使排序为数字)。
或者避免对年份列表进行硬编码并多次抓取目录:
typeset -A files n
for f (**/*_<->.jpg(.LK+1)) {
y=${${f##*_}%.*}
((++n[$y] > 100)) || files[$y]+=$f$'\0'
}
for y (${(k)files}) {
mkdir -p $y && cp -- ${(0)files[$y]} $y
}
(未经测试)