第一个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来。
暂时想到这么多把,希望能继续补充下


浙公网安备 33010602011771号