我有数百张以此模式命名的图像:
file-001.gif
file-002.gif
file-003.gif
...
使用 magick,我想附加每组图像,条件是它们的总大小不超过 950kb。
然后在总大小小于 950kb 时处理下一批,依此类推。
可以在shell脚本中实现吗?
答案1
好吧,我非常有信心这是我遇到过的最糟糕的单线,但我想它会做到这一点。松散地基于https://askubuntu.com/questions/878948/how-do-i-generate-a-running-cumulative-total-of-the-numbers-in-a-text-file
sed $(echo 3+`ls -la | tee out/filesWithSizes.txt | awk '{print $5}' | awk '{total += $0; $0 = total}1' | sed 's/$/ < 950000/' | bc | grep -o '1' | wc -l`|bc )q out/filesWithSizes.txt | tail -n +4
让我解释一下这种疯狂:
用于ls -la
获取所有文件及其大小
用于tee
存储该信息以供将来参考并将其保存在管道中
用于awk
打印第5列(即大小)
使用 moreawk
生成其累积和,保证每个设计都已排序(稍后会变得很重要)
用于sed
替换行尾,< 950000
因此基本上附加该字符串
用于bc
计算这些表达式并将它们转换为布尔值(即 0 或 1)
用于grep -o
仅打印包含 1 的行
由于累积总和是按设计排序的,因此可以计算其输出,并且您知道可以粘贴在一起的文件数(按顺序)
这部分是用 完成的wc -l
。由于我在最初没有 a 标志的情况下无法正常工作,因此ls
我回显 3+我们刚刚计算出的数字bc
以获取文件列表输出的停止点
然后使用sed
命令替换来停止输出文件列表的内容及其大小,这些文件的大小是我们之前用 echo 计算的行数之后保存的
最后用于tail
从所述文件的第一个非标题行开始输出。
这将为您提供可以拼接在一起的所有文件的列表。您只需确保移走已处理的所有文件,并一遍又一遍地重新运行该庞大的 oneliner,直到目录中不再有文件为止...
答案2
如果您需要批量附加的图像最终位于小于 950kB 的对象内,则任何单个图像文件都不能超过相同的截止大小。
第一步:列出所有此类文件及其大小(以字节为单位):
$ find -L /path/to/directory -maxdepth 1 -type f -name "*.gif" -size -950k \
-exec sh -c '\ls -Lgo "$1" | tr -s " " | cut "-d " -f3,7-' sh {} \;
这将在屏幕上打印所有找到的文件的名称及其各自的大小,一行打印,按找到的顺序列出。主要标志find
是:
-L
:跟随符号链接并显示有关链接目标的信息,而不是有关链接本身的信息。如果您不想这样做,请-H
改为使用。-maxdepth 1
:使搜索最多下降1
水平,从 下降/path/to/directory
。根据需要修改号码。请参阅man find
获取更多信息。
请注意,至少在我的bash
5.0 实现中,find
似乎可以使用千字节 (kiB) 来运行。换句话说,上面将找到大小小于或等于 950*1024 字节的文件。如果您坚持将 950 KB (950000 B) 作为您的截止大小,没问题:只需使用-size 927730c
即可。在 Debian 发行版上,这个问题似乎早在 2016 年就已经得到解决,所以你可以继续使用-size 950k
......
“尺寸”可以是一个不明确的属性。由于OP中缺乏更多信息,我还包括您可以使用的事实总分配块大小对于每个发现的文件。如果这就是您想要/需要的,那么请执行以下操作:
$ find -L /path/to/directory -maxdepth 1 -type f -name "*.gif" -size -950k \
-exec sh -c '\ls -Ls -C1 "$1"' sh {} \;
您从中获得的大小数字将取决于存储介质的块大小。
第二步magick
:通过 shell 脚本batch_append.sh
一次性处理先前的输出。按列出的顺序附加图像文件,但结果对象的重量不得超过 950kB。该命令的输出find
用于填充数组,如下所示:
$ cat batch_append.sh
#!/usr/bin/bash
sdir=/path/to/directory
size=0
nas_idx=0 # next append start (nas) index (idx)
cnt=0
declare -a b
IFS=$'\n' read -d '' -a a < <(find -L "$sdir" -maxdepth 1 -type f -name "*.gif" -size -950k -exec sh -c '\ls -Lgo "$1" | tr -s " " | cut "-d " -f3,7-' sh {} \; 2>/dev/null)
num=${#a[@]}
for (( i=0; i<num; i++ )); do
(( size+=$(echo "${a[$i]}" | cut "-d " -f1) ))
if [ "$size" -gt 950000 ] ; then
(( size-=$(echo "${a[$i]}" | cut "-d " -f1) ))
(( i-=1 ))
#echo "$size" for files from "$(( nas_idx+1 ))" to "$(( i+1 ))" # testing
#echo "${b[@]}" # testing
magick "${b[@]}" append appended_$(( nas_idx+1 ))_$(( i+1 )).gif
nas_idx=$(( i+1 ))
size=0 # reset 'size' variable to start new append batch
cnt=0
unset b
else
b[cnt]=$(echo "${a[$i]}" | cut "-d " -f2-)
(( cnt++ ))
fi
if (( i==num-1 )); then
# last append batch
#echo "$size" for files from "$(( nas_idx+1 ))" to "$(( i+1 ))" # testing
#echo "${b[@]}" # testing
magick "${b[@]}" append appended_$(( nas_idx+1 ))_$(( i+1 )).gif
unset b
fi
done
printf '\n %d files were processed.\n' "$num"
exit(0)
结束语:
- 在运行脚本之前使脚本可执行。
chmod ug+x batch_append.sh
- 您只需运行该脚本一次。
- 该脚本尚未经过
magick
. - 对于生产环境,应该包含许多陷阱。
- 如果您的主机有多个内核并且需要处理大量文件,则可以改进脚本,也许可以通过安排并行执行来改进。
- 文件在到来时即被批处理,即当
find
cmd 对它们进行统计时。这将取决于搜索标志的数量以及您在一行中的排序方式find
。