Linux程序设计之shell

本文介绍Shell及Shell脚本的基础知识,包括Shell的历史、Shell脚本的特点与应用、基本语法如变量与控制结构,以及常用命令如find、grep等。通过一个具体的CD管理脚本实例,展示了如何使用Shell脚本来实现自动化管理和数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Shell简介
        Shell是一个作为用户与linux操作系统之间的接口的程序,它允许用户向操作系统输入需要执行的命令。Shell执行shell程序,这些程序通常称为脚本,它们是运行时解释执行的。
            
       版本查询:/bin/bash–version
       历史版本
sh(Bournce)      源于UNIX早期版本的最初的shell
csh、tcsh、zsh    C shell及其变体,是继bash和Korn shell之后的第三个最流行的sehll
ksh、pdksh        Korn shell和它的公共域兄弟pdksh由David Korn编写,是许多商业版本UNIX的默认shell
bash	          来自GNU项目的Bourne Again Shell是linux主要的shell。优点是可以免费获取其源代码,即使没有运行它,它可能已经移植到系统中
    注:大多数linux发行系统中,默认的shell程序/bin/sh实际上是/对程序bin/bash的一个连接
二、Shell Script简介
    Shell script是利用shell的功能所写的一个程序,它是使用纯文本文件,将一些shell的语法与命令写在里面,搭配正则表达式、管道命令和数据重定向等功能,已达到我们的目的。Shell script用在系统管理上面是很好的一项工具,但是用在处理大量数值运算上,就不够好了。因为shell script的速度较慢,且使用的cpu资源较多,造成主机资源的分配不良。
    Shell script优点
    1、自动化管理的依据。例如系统每天都会自动查询登陆文件、追踪流量、监控用户主机状态、各项硬件设备状态等工作,都是通过脚本自动执行的。
    2、追踪和管理系统的重要工作。例如,我们想重新启动系统注册表文件,可以使用“/etc/init.d/syslogd restart”,syslogd就是script。
    3、简单入侵检测功能。
    4、连续命令的单一化。例如需要执行很多相似的命令,就可以使用script实现。
    5、简单的数据处理。例如使用awk可以对数据进行处理
    6、跨平台支持与学习历程较短。因为几乎所有的UNIX系统都会使用shell script。
    缺点:虽然shell script号称是程序,但是处理数据的速度上是不够的,因为shell script用的是外部命令与bash shell的一些默认工具,所以,它经常会调用外部函数库,因此周期长。
三、Shell 编程语法
    1、变量
       在shell里面,变量通常并不需要事先声明,只是通过使用它们来创建它们。默认情况下,所有变量都被看作字符串并以字符串来存储,即使被赋值为数值时也是如此。在shell中,可通过在变量前面加一个$符号来访问它的内容。增加变量内容,使用“$var”或者${var}
       注:如果字符串里包含空格,就必须用引号把它们括起来。此外,等号两边都不能有空格。因此字符串通常都被放在双引号中,以防止变量被空白符分开,同时允许$拓展。
   (1) 使用引号
       将$变量表达式放在双引号中,程序自动替换引号中变量的值,如果放在单引号 中,不会发生替换现象。
          
   (2)环境变量
       主要的环境变量(自定义变量=局部变量   系统变量=全局变量)
$HOME	           当前用户的家目录
$PATH	           以冒号分隔的用来搜索命令的目录列表
$PS1	           命令提示符,通常是$字符。
$PS2	           二级提示符,用于提示后续的输入,通常是>字符
$IFS	           输入域分隔符。Shell输入时用它来分隔字符,通常是空格、制表符或换行符
$0	               Shell脚本的名称
$#	               传递个脚本的参数个数
$$	               Shell脚本的进程号,通常用它生成唯一的临时文件
?	               上一个任务的回传码
export	           自定义变量转成环境变量
    (3)参数变量
$1,$2…..
脚本程序的参数
$*
在一个变量中列出所有的参数,各个参数之间用IFS分隔开。若IFS被修改,则$*将命令行分隔为参数的方式将随之改变。
$@
$*的变体,不适用IFS
    (4)变量的读取
       变量键盘读取、数组与声明:read、array、declare
       Read:读取来自键盘输入的变量    参数 –p(后面接提示符)  -t(等待时间)
       Array:   echo  “${var[1]}”  “${var[2]}”  
       Declare/typeset:  声明变量的类型
                   
     2、布尔判断命令[或test
      test  -f   file  等价于 [  -f file  ]
      注:使用[]时每个组件之间都需要空格来分隔,中括号里面的变量最好使用“”括起来,常量最好都以‘‘或“”括起来。
      条件类型: 
string 1  = string2     如果两个字符串相同则结果为真
string 1 != string2     如果两个字符串不相等则为真
    -n  string          字符串不为空则为真
    -z  string          字符串为空则
文件条件测试:
-d  file	判断文件名是否存在且为目录
-e  file	判断文件名是否存在
-f  file	判断文件名是否存在且为文件
-g  file	判断文件名是否存在且具有“SGID”的属性
-u  file	判断文件名是否存在且具有“SUID”的属性
-r  file	判断文件名是否存在且具有“可读”的权限
-x  file	判断文件名是否存在且具有“可执行”的权限
-w  file	判断文件名是否存在且具有“可写”的权限
-s  file	判断文件名是否存在且为“非空白文件”
-p  file	判断文件名是否存在且为一个FIFO(pipe)文件
-b  file	判断文件名是否存在且为一个block device设备
-c  file	判断文件名是否存在且为一个character device设备
-L  file	判断文件名是否存在且为一个连接文件
算术比较:
expression1  -eq   expression2	两个表达式相等则为真
expression1  -ne   expression2	两个表达式不相等则为真
expression1  -gt   expression2	expression1大于expression2为真
expression1  -ge   expression2	expression1大于等于 expression2为真
expression1  -lt   expression2	expression1小于 expression2为真
expression1  -le   expression2	expression1小于等于 expression2为真
    !  expression	        表达式为假则结果为真
多重条件判断,例如test  -r filename   -a  -x filename
-a	两个条件同时满足
-o	两个条件满足任意一个
!	反向状态
3、控制结构
   (1)if语句
      格式:
if  condatation                if   condtaion  then
then                               statements
    statements      ======     elif 
elif                               statements
    statements                 else
else                               statements
    statements                 fi
fi
      例:  if  [  -f file  ];  then
   (2)for语句
      格式:    
for  variable in  values
do
    statements
 done
      注:使用通配符可以拓展语句
      例如:循环从1加到100       for  variable in  $ ( seq 1 100 );
   (3)while语句
      格式: 
while  condation do
    statements
done
   (4)until语句
      格式: 
until  condaion
do
    statements
 done 
   (5)case 语句
      格式: 
case  variable in
    pattern  [ |  pattern  ]  …)  statements;;
    pattern  [ |  pattern  ]  …)  statements;;
     …..
esac   
   (6)命令列表
      AND列表:statement1  &&  statements2 &&  statements3  && ….
      OR  列表:statements1  ||   statements2   ||   statements3 ||  … 
   (7)语句块
      在某些只能使用单个语句的地方,可以使用语句块实现多条语句的执行。
    4、函数
       格式:   
function  fname() {
    .....
}
四、命令
    1、break:可以在控制条件未满足之前,跳出for、while或until循环。
    2、””命令:它是一个空命令。偶尔用于简化条件逻辑,相当于true的别名。
               while  true   =====   while  :
    3、continue:这个命令使for、while或until循环跳到下一循环继续执行。
    4、”.” 命令:用于在当前shell中执行命令
      通常,当一个脚本执行一条外部命令或脚本程序时,会启动一个新的shell,命令将在这个新环境中执行,执行完毕之后,环境会被丢弃,留下退出码返回给父shell。  但外部source命令和点命令在执行脚本程序中列出的命令时,使用的是调用该脚本程序的同一个shell。
    5、echo命令:用于输出。
      常见的一个问题是去掉换行符解决方式有:
               echo  -n  “string  to output”
               echo  -e  “string  to  output   \c” 
    6、eval命令:允许对参数求值。
    7、exit  n命令:使脚本程序以退出码n结束运行。    
126                 文件不可执行
127                 命令未找到
128及以上           出现一个信号
    8、export命令:将作为它参数的变量导出到子shell中,并使之在子shell中有效。默认情况下,在一个shell中被创建的变量在这个shell调用的下级(子)shell中是不可用的。Export命令把自己的参数创建为一个环境变量,而这个环境变量可以被当前程序调用的其他脚本和程序看见。从更技术的角度来说,被导出的变量构成从该shell衍生的任何子进程的环境变量。
    9、expr  ==  $((……))   用于算术替换,而$( )用于命令的执行和获取输出。
    10、trap命令:用于指定在接收到信号后将要采取的行动。
        一种常见用途是在脚本程序被中断时完成清理工作。     
trap command   signal     
trap   "rm -f temp_file"   EXIT
    11、find命令:用于搜索文件的命令。
        语法:  find  [path] [options]  [tests]   [actions]       
选项	                含义
-depth	          在查看目录本身之前先搜索目录的内容
-follow	          跟随符合链接
-maxdepths N	  最多搜索N层目录
-mount	          不搜索其他文件系统中的目录
       注:通常使用圆括号来强制测试和操作符的优先级
    12、grep(通用正则表达式解析器  General Regular ExpresiionPraser):在文件中搜索字符串
       语法:  grep  [options] PATTERN  [FILES]                            
options:
-c	输出匹配行的数目,而不是输出匹配的行
-E	启用拓展表达式
-h	取消每个输出行的普通前缀,即匹配查询模式的文件名
-i	忽略大小写
-l	只列出包含匹配行的文件名,而不是输出真正的匹配行
-v	取反操作    
13、正则表达式,通常配合grep使用
       常见的特殊字符          
^	指向一行的开头
$	指向一行的结尾
.	任意单个字符
[]	方括号内包含一个字符范围,其中任何一个字符都可以被匹配
               特殊匹配模式
[:alnum:]
字符与数字字符
[:alpha:]
字母
[:ascii:]
ASCII字符
[:blank:]
空格或制表符
[:cntrl:]
ASCII控制字符
[:digit:]
数字
[:graph:]
非控制、非空格字符
[:lower:]
小写字母
[:print:]
可打印字符
[:punct:]
标点符号字符
[:space:]
空白符
[:upper:]
大写字符
[:xdigit:]
十六进制数字
 
 
五、Shell script编写
#!/bin/bash

 

#This is a program about CD collection

#Copyright (C) 2014

 

#command line description

#wc命令

 #wc(Word Count)命令的功能为统计指定文件中的字节数、字数、行数

  #并将统计结果显示输出

  #-c统计字节数。

  #-l统计行数

  #-m统计字符数。这个标志不能与-c 标志一起使用

  #-w统计字数

#grep命令

  #通用正则表达式解析器

  #在文件中搜索字符串

 #grep [options] PATTERN [FILES]

 #options

 #-c   输出匹配行的数目

 #-E   启用拓展表达式

 #-h   取消每个输出行的普通前缀

 #-i   忽略大小写

 #-l   只列出包含匹配行的文件名

 #-v   对匹配模式进行取反操作

#cut命令   

 #cut是一个选取命令,就是将一段数据经过分析,取出我们想要的。一般来说,选取信息通常是针对            “行”来进行分析的,并不是整篇信息分析的。

 #cut  [-bn] [file] 或 cut [-c] [file]  或  cut [-df] [file]

 #cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出。

  #如果不指定 File 参数,cut 命令将读取标准输入。必须指定 -b、-c 或 -f 标志之一。

  #主要参数

  #-b:以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。

  #-c:以字符为单位进行分割。

  #-d:自定义分隔符,默认为制表符。

 #-f  :与-d一起使用,指定显示哪个区域。

  #-n:取消分割多字节字符。仅和-b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的<br />范围之内,该字符将被写出;否则,该字符将被排除。

 

 

#set envrironment variable

menu_choice=""

current_cd=""

title_file="title.cdb"

tracks_file="tracks.cdb"

temp_file="cdb.$$"

 

trap 'rm -f $temp_file' EXIT

 

get_return(){

   echo -e "Press return \c"

   read x

   return 0

  }

 

get_comfire(){

   echo -e "Are you sure? \c"

   while true

   do

     read x

     case "$x" in

       y | yes | YES | Yes )

         return 0;;

       n | NO |  No | N ) 

         echo "Canclled!"

         return 1;;

       * ) echo "Pleasse enter yes or no!"

     esac

   done

  }

 

# Menu

set_menu_choice(){

   clear

   echo "Options :-"

   echo

   echo "  a) Add new CD"

   echo "  f) Find CD"

   echo "  c) Count the CDs andtracks in the catalog"

 

   if [ "$cdcatnum" != "" ]; then

     echo "  l) List tracks on$cdtitle"

     echo "  r) Remove$cdtitle"

     echo "  u) Update trackinformation for $cdtitle"

   fi

   

   echo "  q) Quit"

   echo

   echo -e "Please enter choice then press retrun \c"

   read menu_choice

   return

  }

 

insert_title(){

#insert information to title_file

   echo $* >> $title_file

   return

  }

 

insert_track(){

   echo $* >> $tracks_file

   return

  }

 

 

add_record_tracks(){

   echo "Enter track information for this CD"

   echo "When no more tracks enter q"

   cdtrack=1

#define the title of track

   cdttitle=""

#set $cdttitle = null

   while [ "$cdttitle" != q ]

   do

#input title

     echo -e "Track $cdtrack, track title? \c"

     read tmp

     cdttitle=${tmp%%,*}

     if [ "$tmp" != "$cdttitle" ]; then

       echo "Sorry, no commas allowed"

       continue

     fi

 

     if [ -n "$cdttitle" ]; then

       if [ "$cdttitle" != "q" ]; then

         insert_track $cdcatnum,$cdtrack,$cdttitle

       fi

     else

       cdtrack=$((cdtrack-1))

     fi

     cdtrack=$((cdtrack+1))

   done

  }

  

add_records(){

 

   echo -e "Enter catalog name \c"

   read tmp

   cdcatnum=${tmp%%,*}

   

   echo -e "Enter title \c"

   read tmp

   cdtitle=${tmp%%,*}

   

   echo -e "Enter type \c"

   read tmp

   cdtrack=${tmp%%,*}

   

   echo -e "Enter artist/composer \c"

   read tmp

   cdac=${tmp%%,*}

   

   

   #Check that they want to enter the information

   echo About to add new entry

   echo "$cdcatnum $cdtitle $cdtype $cdac"

   

   #if comfired then append it to the tille_file

   if get_comfire ; then

      insert_title $cdcatnum,$cdtitle,$cdtype,$cdac

     add_record_tracks

   else

 

     remove_records

   fi

   

   return

  }

 

 

find_cd(){

   if [ "$1" = "n" ]; then

     asklis=n

   else

     asklist=y

   fi

   

   cdcatnum=""

   echo -e "Enter a string to search for in the  CD titles \c"

   read searchstr

   if [ "$searchstr" = "" ]; then

     return 0

   fi

  

 

   if [ ! -e $temp_file ]; then

       echo"temp_file  don'texist!!!!!!!!"

   fi

 

   grep "$searchstr" $title_file > $temp_file

   

   set $(wc -l $temp_file)

   linesfound=$1

   

   case "$linesfound" in

     0 ) echo "Sorry, nothing found"

         get_return

         return 0

         ;;

     1 ) ;;

     2 ) echo "Sorry, not unique."

         echo "Found the following"

         cat $temp_file

         get_return 0

         ;;

   esac

   

   IFS=","

   read cdcatnum cdtitle cdtype cdac < $temp_file

   IFS=" "

   

   if [ -z "$cdcatnum" ]; then

     echo "Sorry, could not extract catalog filed from $temp_file"

     get_return

     return 0

   fi

   

   echo

   echo Catalog number: $cdcatnum

   echo Title: $cdtitle

   echo Type: $cdtype

   echo Artist: $cdac

   echo

   

   get_return

   

   if [ "$asklist" == "y" ]; then

     echo -e "View tracks for this CD? \c"

     read x

     if [ "$x" == "y" ]; then

       echo

       list_tracks

       echo

     fi

   fi

   return 1

  }

 

 

update_cd(){

 

   if [ -z "$cdcatnum" ]; then

     echo "You must select a CD"

     find_cd n

   fi

   

   if [ -n "$cdcatnum" ]; then

     echo "Current tracks are:-"

     list_tracks

     echo

     echo "this will re-enter the tracks for $cdtitle"

     get_comfire && {

         grep -v "^${cdcatnum}," $tracks_file > $temp_file

         mv $temp_file $tracks_file

         echo

         add_record_tracks

       }

   fi

   return

  }

 

 

 

count_cds(){

   #wc -l file :  statistic thecounts

   set $(wc -l $title_file)

   

   num_titles=$1

   set $(wc -l $tracks_file)

   num_tracks=$1

   echo found $num_titles CDs with a total of $num_tracks tracks

   get_return

   return

  }

 

 

 

remove_records(){

   

   if [ -z "$cdcatnum" ]; then

      echo "You must select a CD"

     find_cd n

   fi

 

   if [ -n "$cdcatnum" ]; then

     echo "You are about to delete $cdtitle"

     get_comfire && {

         grep -v "^${cdcatnum}," $title_file > $temp_file

         mv $temp_file $title_file

          

         grep -v "^${cdcatnum}," $tracks_file > $temp_file

         mv $temp_file $tracks_file

         cdcatnum=""

         echo Entry removed

       }

     get_return

   fi

   return

  }

 

 

list_tracks(){

    

   if [ "$cdcatnum" == "" ]; then

     echo "No CD selected yet"

     return

   else

     grep -v "^${cdcatnum}," $tracks_file > $temp_file

     num_tracks=$(wc -l $temp_file)

     if [ "$num_tracks" = "0" ]; then

       echo no tracks found for $cdtitle

     else {

       echo

       echo "$cdtitle :-"

       echo

       cut -f 2- -d , $temp_file

       echo

     } | ${PAGER:-more}

     fi

   fi

   get_return

   return

  }

 

 

#rm -f $temp_file

if [ ! -f $title_file ]; then

 touch $title_file

fi

 

if [ ! -f $tracks_file ]; then

 touch $tracks_file

fi

 

if [ ! -f "$temp_file" ]; then

 touch $temp_file

fi

 

#Now the application proper

 

clear

echo

echo

echo "Mini CD manager"

sleep 1

 

quit=n

 

if [ -e $temp_file ]; then

   echo "temp_file don't exist!!!"

   return EXIT

fi

 

while [ "$quit" != "y"]; do

 set_menu_choice

 case "$menu_choice" in

    a) add_records;;

    r) remove_records;;

    f) find_cd y;;

    c) count_cds;;

    l) list_tracks;;

    b)

     echo

     more $title_file

     echo

     get_return;;

    q| Q ) quit=y;;

    *) echo "Sorry, choice not recoginzed";;

 esac

done

 

#Tidy up and leave

 

rm -f $temp_file

echo "Finished"

exit 0

运行结果
显示主目录

添加CD

添加成功之后返回结果:

查找CD:

删除CD:
 
结束脚本:
 
              


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值