Linux shell 学习笔记(二)

实战!

指定一个目录,输出该目录下所有文件个数,文件大小之和,以及<=4096字节的文件大小之和及个数,目录名称通过参数传给shell脚本。
 

第一次尝试!

###############
# Date: 2017-03-02
# Version: V1.0
# Author: lizp
# Usage: FileInfoCount
################

if [ -d $1 ]
then
  ls $1 -lR | awk 'BEGIN {size=0;count=0;Vsize=0;Vsize=0;} {if($5 != ""){size=size+$5;count=count+1;if($5 <= 4096){Vsize=Vsize+$5;Vcount=Vcount+1;}}} END{print "size is", size, "
count is", count, "
Vsize is",Vsize,"
Vcount is", Vcount}'
else
    echo "input path does not exist"
    exit
fi

shell脚本的if语句中, “-d”之前必须有空格,“$1”之后必须有空格,if后面必须有空格

-d 如果$1为目录,则“-d $1”为真。

ls -lR  展示输入目录下的所有的文件及文件夹(包含子目录及子目录下的文件夹)。

*** | awk 'BEGIN{} {} END{}'   将“***”中的记录按照换行符“ ”分割,然后逐条执行:先执行BEGIN段中的部分,然后执行中间大括号中的内容,执行完成后,执行END中的部分。

运行:

FileInfoCount.sh  /mydir

运行结果:

size is 15727 
count is 7 
Vsize is 15727 
Vcount is 7

  结果:题目要求是统计当前目录下所有子目录中的文件的个数,不包括文件夹!

第二次尝试!

 既然要求只统计目录下的文件个数,就用递归吧,思路很简单,实现起来却很复杂。

#########################################################################################################################
# Date: 2017-03-02
# Version: V1.0
# Author: lizp
# Usage: FileInfoCount
#########################################################################################################################+
size=0
count=0
Vsize=0
Vcount=0

fileInfo()
{
    echo "file Info Process begin..."
    size=`ls $1 -l | awk '{printf "%ld
", v1+$5}' v1=$size`
    count=`ls $1 -l | awk '{printf "%ld
", v1+1}' v1=$count`
    Vsize=`ls $1 -l | awk '{if($5 <= 4096){printf "%ld
", v1+$5}}' v1=$Vsize`
    Vcount=`ls $1 -l | awk '{if($5 <= 4096){printf "%ld
", v1+1}}' v1=$Vcount`
    resultDisplay
    echo "file Info Process end..."
}

doOneProcess()
{
    echo "process begin, path is $1"
    ls $1 | grep -v "match: no file" |while read filename
    do
      filepath="$1""/""${filename}"
      echo $filepath
      
      if [ -d $filepath ]
        then
          doOneProcess $filepath
        else
            fileInfo $filepath
        fi
    done
    resultDisplay
    echo "process end..."
}

resultDisplay()
{
    echo "size is" $size
  echo "count is" $count
  echo "Vsize is" $Vsize
  echo "Vcount is" $Vcount
}


if [ -d $1 ]
then
  if [ -z $1 ]
  then
    path=`pwd`
  else
      path=$1
  fi
  
  doOneProcess $path
  resultDisplay
else
  echo "input path does not exist"
  exit
fi

跑了下,结果却是错的!

分析原因:管道内的全局变量是不会返回给管道入口的程序的,换句话说,进入管道后,全局变量会复制一份给子进程,然后无论在子进程里值如何变化,在父进程中的值还是原来的模样,因为管道传的是值,不是传址。

最终

咨询导师,发现可以用find命令,很简单的就可以实现!

#########################################################################################################################
# Date: 2017-03-02
# Version: V1.0
# Author: lizp
# Usage: FileInfoCount
#########################################################################################################################+
if [ -d $1 ]
then
   find $1 -type f -ls | awk 'BEGIN {size=0;count=0;Vsize=0;Vsize=0;} {if($7 != ""){size=size+$7;count=count+1;if($7 <= 4096){Vsize=Vsize+$7;Vcount=Vcount+1;}}} END{print "size is", size, "
count is", count, "
Vsize is",Vsize,"
Vcount is", Vcount}'
else
    echo "input path does not exist"
    exit
fi

哇!原来这么简单!shell真的博大精深!还有很长的路要走啊。。。

可是,用find命令可以实现,那么用递归就不行了吗?管道不能回传全局变量,这个问题就无解了吗?

于是我又尝试用临时文件来存放管道输出变量,实现方式如下:

#########################################################################################################################
# Date: 2017-03-02
# Version: V2.0
# Author: lizp
# Usage: FileInfoCount
#########################################################################################################################+

tmpoutfile="/tmp/file_$$"

fileInfo()
{
    fileSize=`ls $1 -l | awk '{print $5}'`
    echo $fileSize >> $tmpoutfile
}

doOneProcess()
{
    ls $1 | grep -v "match: no file" |(while read filename
    do
      filepath="$1""/""${filename}"
      
      if [ -d $filepath ]
        then
          doOneProcess $filepath
        else
            fileInfo $filepath
        fi
    done)
}

resultDisplay()
{
  if [ -f $tmpoutfile ]
  then
  cat $tmpoutfile | awk  'BEGIN{size=0;count=0;Vsize=0;Vcount=0;} {size=size+$1;count=count+1;if($1 <= 4096){Vsize=Vsize+$1;Vcount=Vcount+1;}} END{printf("size is %s,count is %s,Vsize is %s,Vcount is %s
",size,count,Vsize,Vcount)}' 
  fi
}


if [ -d $1 ]
then
  if [ -z $1 ]
  then
    path=`pwd`
  else
      path=$1
  fi
  
  doOneProcess $path
  resultDisplay
else
  echo "input path does not exist"
  exit
fi

成功了,输出结果如下:

/$test.sh
size is 8090,count is 5,Vsize is 8090,Vcount is 5

其中size和count为当前目录下的文件(不包含文件夹)的总大小及数量,Vsize和Vcount为当前目录下的文件(不包含文件夹)中,大小<=4096的文件的总的大小及个数。

这样一来,我就有了两种解决方法:

1. 通过find命令直接罗列出所有文件,用awk进行计算。

2. 通过ls命令罗列出所有文件及文件夹,通过递归的方式进行处理,为了防止管道中运算得到的变量丢失,其中管道的输出结果会输出到一个临时文件中去,最后通过awk去计算临时文件中记录的数据,得出结果。

原文地址:https://www.cnblogs.com/lzp666/p/6496610.html