第一个shell 程序

这两个月都有不时的学习shell命令和编程,但是一直都停留在记忆命令,以及弄清命令输出意义阶段,没怎么进行过实际shell脚本编程,一直就认为就几个命令而已,很容易学习。不过自己一动手,各种错误摆出,弄得自己莫名其妙,弄了一个下午,才勉强算是把一个小脚本实现了把。编程这东西,纸上得来终是浅啊。这个脚本要求来源于广研的一道笔试题目:

写一个SHELL,可以对某个文件夹里面的文件进行归类,默认分界线是100K,把文件夹中大于分界线的文件拷贝到文件夹1,然后把小于等于的文件拷贝到文件夹2。用户可以用-s 命令改变分界线的大小,比如-s 200k。用户也可以用-z命令,是文件拷贝前先进行压缩操作。SHELL写完后,用户执行的格式为:./<shell的文件名> [-s <filesize>] [-z] <待测试文件夹> <大文件的文件夹> <小文件的文件夹>。 例如 ./test.sh –s 200k –z /testfloder /newfloder1 /newfloder2

直接先上代码把:

category.sh
 1 # 广研笔试题目 把目标文件夹里的文件根据 文件大小 分在 两个目录下
 2 #目标目录和文件大小 以及分类后的两个目录均有参数给出,-s文件大小 默认为100K
 3 #-z参数代表拷贝时要进行压缩
 4 #命令格式:./<shell文件名> [-s 200k] [-z] <目标目录> <大文件目录> <小文件目录>
 5 #!bin/sh
 6 function error()
 7 {
 8     echo "$@" 1>&2    
 9     echo "args format error! usage:./category [-s 200k] [-z] <sourcedir> <bigdir> <smalldir>"
10     exit 1
11 }
12 zflag=0
13 default=100
14 sourcedir=""
15 bigdir=""
16 smalldir=""
17 #读入参数-s,-z
18 while [ $# -gt 0 ]
19 do
20     case $1 in
21     -s) default=$(echo $2 | grep '[1-9][0-9]*[kK]' | tr -cd '0-9') #只保留大小,不保存单位KB
22         if [ -z default ];then 
23                 error
24         fi 
25         shift
26         ;;
27     -z) zflag=1;;
28     -*) "Unrecognized option : $1 "
29         error ;;
30     *) break;;
31     esac
32     shift
33 done
34 
35 #读入路径信息
36 if [ $# -eq 3 ];
37     then
38          sourcedir=$1;
39           bigdir=$2;
40           smalldir=$3;
41         
42     else error
43 fi
44 #源路径是否存在
45 if [ ! -d $sourcedir ];
46     then  echo "sourcedir not exists "
47           exit 1 
48 fi
49 
50 if [ ! -d $bigdir ];
51         then  $(mkdir $bigdir) || exit 1 
52 fi
53 
54 if [ ! -d $smalldir ];
55     then  $(mkdir $smalldir) || exit 1 
56 fi
57 #读取源目录下的文件信息 ,注意block-size应设为kB, 并从第二行开始保留在临时文件中 /tmp/tmp.tmp,因为第一行是源目录的size,所以去掉
58 $(ls -l --block-size=k $sourcedir | sed -n '2,$p' > /tmp/tmp.tmp)
59 
60 if [ $? -eq 1 ]; then
61                    echo "can't access to $sourcedir"
62                    exit 1
63 fi
64 #从临时文件一行一行的读取文件信息
65 while read line
66 do
67      filesize=$(echo $line | cut -d ' ' -f 5 | tr -cd '0-9' ) #拿到文件大小
68      filename=$(echo $line | cut -d ' ' -f 9)              #拿到文件名
69      sourcefilename=${sourcedir}${filename}
70      #echo $sourcefilename
71      #echo $filesize
72     if [ -f $sourcefilename ];then     #如果是普通文件  
73           if [ $filesize -gt $default ];then
74                         targetfilename=${bigdir}${filename}
75            else targetfilename=${smalldir}${filename}
76         fi
77         if [ $zflag -eq 1 ];then
78             $(gzip -c $sourcefilne > ${targetfilename}.gz)
79         else
80            echo $targetfilename
81                    $(cp $sourcefilename $targetfilename)
82         fi
83     fi
84 done <  /tmp/tmp.tmp
85 rm /tmp/tmp.tmp #删除临时文件
86     

 

下面在总结下写代码中遇到的问题:
 
1. 大部分的语句 都可以不加分号 进行区分 ,但是 if后必须加分号if [] ;   , case 后 也必须加分号,而且是两个;;。
2.在if then语句时,受C语言影响,老喜欢在加大括号。{} 大括号不能随便加,因为它是shell里的特殊字符,  不要老想着在then 后面 加 { 语句块 } ,这是错误的。
3.就是循环的读入的问题: 不能在while read  line  < * (代表文件名),这会造成只能打开文件的第一行,并且不停地打开关闭文件,造成死循环;应该 done后面 进行 输入重定向 或者 利用 管道 cat * | while read line,不过建议利用前一种。
4.还是上面一个问题,  在done 后面进行重定向时, *好像只能是一个文件,而不能是一个命令的输出。 因为本来在tmp中我存储的是 ls -l --block-size=K ***的全部信息,然后想把$(sed -n ‘2,$p' /tmp/tmp.tmp) 输出作为重定向的循环输入,但是老是会报错(老说重定向 模糊)。无奈下只能在保存tmp.tmp临时文件时,直接把第一行的信息删掉
      后面查阅到:应该输入输出的重定向格式问题,

        格式:  command-line [n] < >  file或文件描述符&设备

       重定向输入时,只能用file或文件描述符或设备,不能用命令行作为输入

5.在这个shell中,涉及到一个字符串 去 取出部分子串的问题,即100K,或123144K,取出数字部分。像这种问题,(1)简单的则可以通过 tr 命令(不支持正则表达),删除掉不要的字符,(2)复杂些则需要通过sed命令扩展的正则表达式 替换动作,sed -n 's/.* \([0-9]\{1,\}\)M free.*$/\1/p'  替换。(3) 还可以通过赋值给变量,利用变量(例如tmp)内容来删除或替换。如果你要删除的内容从最后一个字符开始,例如tmp=100k,则可利用 tmp=${tmp%k}。如果你要删除的内容从第一个字符开始,例如tmp=100k,你要删除1,则tmp=${tmp#1},  此时tmp变为了00k。 
          需要注意的是当你想删除的字符串不在 变量的 开头 或 结尾 时,就不能用删除,只能用 替换 。用空串““替换掉你想删除的字符串来完成删除功能。${变量/旧字符串/新字符串/},这里也可以利用tmp=${tmp/k/""}也可以提取出100来。
暂时想到这么多把,希望能继续补充下
 
 
 
posted @ 2012-09-23 22:36  忧伤还是欢乐  阅读(186)  评论(0)    收藏  举报