一、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:

结束脚本:
