第二章:Improving On User Commands--18.显示目录内容

   跳着来,一篇一篇移,很麻烦呀。

   ls命令是Unix命令行的基础,该命令中有一个元素在我而言不得要领:指示目录的大小。当一个目录被列出来的时候,程序要么是列出文件的内容,要么是显示文件数据的1024字节块的数目。一个典型的ls -l的输出如下:

drwxrwxr-x 2 taylor taylor 4096 Oct 28 19:07 bin

 

   但是,事实上,这些内容并不是很有用处,因为我想要知道的是给定目录中有多少个文件。这就是这个脚本要达到的目的,生成一个漂亮的多层文件、目录列表,同时显示了文件大小或是目录中的文件数。

代码:

#!/bin/sh
 
 # formatdir.sh -- 输出一个界面友好、功能有效的目录列表
 
 gmk()
 {
     # 输入单位Kb,输出单位Kb,最大的单位是Gb
     if [ $1 -ge 1000000 ]; then
         echo "$(scriptbc.sh -p 2 $1 / 1000000)Gb"
     elif [ $1 -ge 1000 ]; then
         echo "$(scriptbc.sh -p 2 $1 / 1000)Mb"
     else
         echo "${1}Kb"
     fi
 }
 
 if [ $# -gt 1 ]; then
     echo "Usage: $(basename $0) [dirname]" >&2
     exit 1
 elif [ $# -eq 1 ]; then
     cd "$@"
 fi
 
 for file in *
 do
     if [ -d "$file" ]; then
         size=$(ls "$file" | wc -l | sed 's/[^[:digit:]]//g')
         if [ $size -eq 1 ]; then
             echo "$file ($size  entry)|"
         else
             echo "$file ($size  entries)|"
         fi
     else
         size="$(ls -sk "$file" | awk '{print $1}')"
         echo "$file ($(gmk $size))|"
     fi
 done | \
     sed 's/ /^^^/g' | \
     xargs -n 2 | \
     sed 's/\^\^\^//g' | \
     awk -F\| '{printf "%-39s %-39s\n", $1, $2}'
 
 exit 0

脚本如何工作:
这个脚本最有意思的部分就是gmk函数,它会接受一个以kb为单位的数字,输出的单位有kb、mb、gb。它不会让一个文件显示出2083364kb这样的数字,这个函数会显示的是2.08Gb。注意,gmk函数调用时用的是$()方式:

echo "$file ($(gmk $size))|"

因为在$()中的参数会被放到当前脚本运行shell的子shell中,子shell自动继承当前运行的shell中定义的函数。在靠近该脚本顶部位置,还有一个捷径允许用户给定一个目录,而不只是只能查看当前的目录。它会使用cd命令将当前的工作目录切换到要求的位置。这遵循了优秀shell脚本编程的圣歌:

Where there's a shortcut, there's a better way。

 

这个脚本的主要逻辑部分包含了把脚本输出到平均分布好的2列上。你并不能在输出流中遇到空白时停下来,因为文件或是目录是有可能在它们的名字中包含空白的。为了搞定这个问题,脚本首先用3个脱字符(^^^)替换掉空白。然后使用xargs命令归并2行,这样每2行会变成有一个空白的一行。最后,使用awk命令格式化输出。注意目录中非隐藏的文件数目是很容易计算得到的,只需要用sed清理下wc命令产生的输出:

# 很简单的用sed删掉wc产生的输出中的所有非数字字符
# [:digit:] 是POSIX字符类,匹配数字,又比如[:alpha:] 匹配字母
# [^] 表示不匹配任意一个中括号内的字符
# sed 's/...//g' 这个是sed的典型删除句式,因为后面替换的内容是什么也没有
# 有朋友反映,本书中这种句子用的很多,但不大明了,老七就稍微介绍下
  size=$(ls "$file" | wc -l | sed 's/[^[:digit:]]//g')

运行脚本:
如果想看当前目录,那就无参调用本脚本。想看别的任何目录(只要你有这个权限),只需要做为参数传给本脚本即可。

运行结果:

formatdir.sh  # 无参调用
-hilow.sh(4Kb)                        ANSIColor.sh(4Kb)                     
countCmds.sh(4Kb)                        filelock.sh(4Kb)                      
fmt.sh(4Kb)                              formatdir.sh(4Kb)                     
hilow.sh(4Kb)                            inpath.sh(4Kb)                        
library.sh(8Kb)                          logrm.sh(4Kb)                         
newnicenum.sh(4Kb)                       newrm.sh(4Kb)                         
normdate.sh(4Kb)                         poorEcho.sh(4Kb)                      
scriptbc.sh(4Kb)                         testLibrary.sh(4Kb)                   
test.sh(4Kb)                             tinyscript.sh(4Kb)                    
unrm.sh(4Kb)                             validAlphaNum.sh(4Kb)                 
validdate.sh(4Kb)                        validfloat2.sh(4Kb)                   
validfloat.sh(4Kb)                       validint.sh(4Kb)                      
validtime.sh(4Kb)                                  

formatdir.sh ~/why/test/  # 别的目录,非当前工作目录
beijing(7entries)                        dir(0entries)                         
gw.sh(12Kb)                              gw.txt(4Kb)                           
heilongjiang(1entry)                     remitcheck(5entries)                  
rmdir(5entries)                          test.sh(8Kb)                          
tmp(4entries)

分析脚本:
GNU版本的ls又一个-h选项,它提供了类似的功能。如果你的机器上是这个版本的ls,那么添加上这个选项,然后删除调用gmk会提高脚本的运行速度。
这个脚本中别的值得思考的特性是你是否恰巧碰上有一个用户特别喜欢在命名一个文件时使用3个脱字符,这会导致在输出时产生异样。不过一般不会碰到。我测试过的linux上的文件,至今未碰到过一例。不过,如果你确实很关心这点,你可以不用3个脱字符,只要用别的不会出现在你的文件名中的字符序列就行了。

ps:

   scriptbc.sh,还记得第九个脚本“一个任意精度浮点计算器”吗?

   函数中使用的参数,都是调用传递给它的参数,而不是从命令行传递给脚本的参数。如果想用命令行参数,可以把命令行参数保存到一个变量中,然后在函数中调用。注意,即使如"$@"这样的调用,在函数中也不是命令行参数。

   本脚本中第13行,echo "${1}Kb",使用了大括号,经测试不用也一样行。有的时候,像这种大括号、双引号使用与否皆可的情况,确实让人觉得很困扰。


你可能感兴趣的:(linux,shell,bash)