Shell 枚举文件类型统计信息,Linux支持很多文件类型。如果有一个脚本,它能够遍历目录及其子目录中所有的文件,并生成一份关于文件类型细节以及每种文件类型数量的报告,这肯定很有意思。本章将教你编写这样一个能够遍历大量文件并收集相关细节的脚本。
预备知识
在Unix/Linux系统中,文件类型并不是由文件扩展名决定的(微软的Windows操作系统是这么做的)。Unix/Linux系统使用file
命令,通过检查文件内容来确定其类型。编写这个脚本的目的是从多个文件中收集文件类型统计信息。脚本利用关联数组保存同类文件的数量信息。
bash
在版本4 中才开始支持关联数组
实战演练
按照以下步骤来枚举文件类型统计信息。
(1) 用下面的命令打印文件类型信息:
$ file filename
$ file /etc/passwd
/etc/passwd: ASCII text
(2) 打印不包括文件名在内的文件类型信息:
$ file -b filename
ASCII text
(3) 生成文件统计信息的脚本如下:
# !/bin/bash
# 文件名: filestat.sh
if [ # -ne 1 ];
then
echo "Usage is0 basepath";
exit
fi
path=1
declare -A statarray;
while read line;
do
ftype=`file -b "line" | cut -d, -f1`
let statarray["ftype"]++;
done<(findpath -type f -print)
echo ============ File types and counts =============
for ftype in "{!statarray[@]}";
do
echoftype : {statarray["ftype"]}
done
(4) 用法如下:
$ ./filestat.sh /home/slynux/temp
(5) 输出信息如下:
$ ./filetype.sh /home/slynux/programs
============ File types and counts =============
Vim swap file : 1
ELF 32-bit LSB executable : 6
ASCII text : 2
ASCII C program text : 10
工作原理
该脚本依赖于关联数组statarray
。这个数组用文件类型作为数组索引:PDF、ASCII…。每个索引对应的值是该类型文件的数量。使用命令declare -A statarray
定义关联数组。
脚本由两个循环组成:一个是while
循环,负责处理find
命令的输出;另一个是for
循环,用于迭代statarray
并生成输出。
while
循环的形式如下:
while read line;
do something
done < filename
在这里,我们没有使用文件,而是使用find
命令的输出作为while
的输入。(find $path -type f -print)
就相当于上面的filename(文件名),只不过是用的子进程的输出。
注意,第一个
<
用于输入重定向,第二个<
用于将子进程的输出转换成相应的filename(文件名)(注:这里使用了进程替换)。这两个<
之间有一个空格,因此shell并不会将其解释为<<
操作符。
find
命令使用选项-type f
返回$path
所定义的目录下的文件列表。read
命令一次读取一个文件名。当read
接收到EOF
(文件末尾)时,它返回假,while
命令退出。
在while
循环中,file
命令用于确定文件类型。-b
选项只显示出文件类型(不包含文件名)。
file
命令能够提供很多细节信息,比如图像编码以及分辨率(如果是图像文件的话)。各种细节信息由逗号分隔,例如:
$ file a.out -b
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not
stripped
我们只需要从上面这些信息中提取ELF 32-bit LSB executable
。因此使用-d
,指明以逗号作为分隔符,使用-f1
选择第一个字段。
<(find $path -type f -print)
等同于文件名。只不过它用子进程输出来代替文件名。注意,第一个<
用于输入重定向,第二个<
用于将子进程的输出转换成文件名。在两个<
之间有一个空格,避免shell将其解释为<<
操作符。
在Bash 3.x
及更高的版本中,有一个新操作符<<<
,可以让我们将字符串作为输入文件。利用这个新操作符,可以将loop
循环的done
语句改写成:
done <<< "`find $path -type f -print`"
${!statarray[@]}
用于返回数组的索引列表。