Shell 脚本基础学习笔记
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Shell 脚本基础学习笔记
shell脚本的解释用“#”号,本文档为了习惯方便,解释大都用“//”,也有用“#”的SHELL 最基本的语法
基本元字符集及其含义(2008-05-24)
abc 表示abc 三個連續的字符, 但彼此獨立而非集合. (可簡單視為三個char set)
(abc) 表示abc 這三個連續字符的集合. (可簡單視為一個char set)
a|b 表示單一字符, 或a 或b .
(abc|xyz) 表示或abc 或xyz 這兩個char. set 之一. (註二)
[abc] 表示單一字符, 可為a 或b 或c . (與wildcard 之[abc] 原理相同)
[^abc] 表示單一字符, 不為a 或b 或c 即可. (與wildcard 之[!abc] 原理相同)
^ 只匹配行首
$ 只匹配行尾
* 只一个单字符后紧跟*,匹配0个或多个此单字符
[ ] 只匹配[ ]内字符。
可以是一个单字符,也可以是字符序列。
可以使用-表示[ ]内字符序列范围,如用[ 1 - 5 ]代替[ 1 2 3 4 5 ]
\ 只用来屏蔽一个元字符的特殊含义。
因为有时在s h e l l中一些元字符有特殊含义。
\可以使其失去应有意义
. 只匹配任意单字符
p a t t e r n \ { n \ } 只用来匹配前面p a t t e r n出现次数。
n为次数
p a t t e r n \ { n,\ }含义同上,但次数最少为n
p a t t e r n \ { n,m \ }含义同上,但p a t t e r n出现次数在n与m之间
现在详细讲解其中特殊含义
1、使用句点匹配单字符
例一:beg.n:以beg开头,中间夹一个任意字符。
例二:. . . .X C. . . .:共10个字符,前四个之后为XC
例三:列出所有用户都有写权限的目录或文件:
ls -l |grep ...x..x..x
2、行首以^匹配字符串或字符序列
^只允许在一行的开始匹配字符或单词。
例如,使用ls -l命令,并匹配目录。
$ ls -l | grep ^d
3、在行尾以$匹配字符串或字符
可以说$与^正相反,它在行尾匹配字符串或字符,$符号放在匹配单词后。
例一:列出文件httpd1.conf中所有以单词common结尾的行
$grep common$ httpd1.conf
或
$cat httpd1.conf | grep common$
例二:匹配所有空行:^ $
例三:只返回包含一个字符的行:^.$
4、用\屏蔽一个特殊字符的含义
下列字符一般可以认为是特殊字符:
CODE:
$ . ' " * [] ^ | () \ + ?
QUOTE:
如:
\ .
反斜杠后面的字符不再是特殊字符,而是一个普通字符,即句点。
QUOTE:
假定要匹配包含^的各行,将反斜杠放在它前面就可以屏蔽其特殊含义:
\ ^
QUOTE:
在正则表达式中匹配以* . p a s结尾的所有文件:
\ * \ . p a s
即可屏蔽字符*的特定含义。
5、使用\{\}匹配模式结果出现的次数
使用*可匹配所有匹配结果任意次,但如果只要指定次数,就应使用\ { \ },QUOTE:
此模式有三种形式,即:
pattern\{n\} 匹配模式出现n次。
pattern\{n,\} 匹配模式出现最少n次。
pattern\{n,m\} 匹配模式出现n到m次之间,n , m为0 - 2 5 5中任意整数。
例一:匹配字母A出现两次,并以B结尾:
CODE:
A \ { 2 \ } B
匹配值为A A B
例二:匹配A至少4次:
CODE:
A \ { 4 , \ } B
可以得结果A A A A B或A A A A A A A B,但不能为A A A B。
例三:如给出出现次数范围,例如A出现2次到4次之间:
CODE:
A \ { 2 , 4 \ } B
则结果为A A B、A A A B、A A A A B,而不是A B或A A A A A B等。
例四:假定从下述列表中抽取代码:
QUOTE:
1234XC9088
4523XX9001
0011XA9912
9931Xc3445
格式如下:前4个字符是数字,接下来是x x,最后4个也是数字,操作如下:[ 0 - 9 ] \ { 4 \ }X X[ 0 - 9 ] \ { 4 \ }
QUOTE:
具体含义如下:
1) 匹配数字出现4次。
2) 后跟代码x x。
3) 最后是数字出现4次。
结果如下
QUOTE:
1234XC9088 -no match
4523XX9001 -match
0011XA9912 -no match
9931Xc3445 -no match
经常使用的正则表达式举例
CODE:
^ 对行首
$ 对行尾
^ [ t h e ] 对以t h e开头行
[ S s ] i g n a [ l L ] 对匹配单词s i g n a l、s i g n a L、S i g n a l、S i g n a L [Ss]igna[lL]\. 对同上,但加一句点
[ m a y M A Y ] 对包含m a y大写或小写字母的行
^ U S E R $ 对只包含U S E R的行
[tty]$ 对以t t y结尾的行
\ . 对带句点的行
^ d . . x . . x . . x 对对用户、用户组及其他用户组成员有可执行权限的目录
^ [ ^ l ] 对排除关联目录的目录列表
[ . * 0 ] 对0之前或之后加任意字符
[ 0 0 0 * ] 对0 0 0或更多个
[ iI] 对大写或小写I
[ i I ] [ n N ] 对大写或小写i或n
[ ^ $ ] 对空行
[ ^ . * $ ] 对匹配行中任意字符串
^ . . . . . . $ 对包括6个字符的行
[a- zA-Z] 对任意单字符
[ a - z ] [ a - z ] * 对至少一个小写字母
[ ^ 0 - 9 \ $ ] 对非数字或美元标识
[ ^ 0 - 0 A - Z a - z ] 对非数字或字母
[ 1 2 3 ] 对1到3中一个数字
[ D d ] e v i c e 对单词d e v i c e或D e v i c e
D e . . c e 对前两个字母为D e,后跟两个任意字符,最后为c e
\ ^ q 对以^ q开始行
^ . $ 对仅有一个字符的行
^\.[0-9][0-9] 对以一个句点和两个数字开始的行
' " D e v i c e " ' 对单词d e v i c e
D e [ V v ] i c e \ . 对单词D e v i c e或d e v i c e
[ 0 - 9 ] \ { 2 \ } - [ 0 - 9 ] \ { 2 \ } - [ 0 - 9 ] \ { 4 \ } 对日期格式d d - m m - y y y y
[ 0 - 9 ] \ { 3 \ } \ . [ 0 - 9 ] \ { 3 \ } \ . [ 0 - 9 ] \ { 3 \ } \ . [ 0 - 9 ] \ { 3 \ } 对I P地址格式nnn. nnn.nnn.nnn [ ^ . * $ ] 对匹配任意行
判断/tmp/mybook是否存在.若不存在则创建
[ ! -d /tmp/mybook ]&&mkdir /tmp/mybook&&echo "/tmp/mybook is created!" ||echo "/tmp/mybook is exist!" //这行比较麻烦.有点错都会出问题.注意方括号内的两边有空格.该空格的地方要空格
shell使用两种引号和反斜线
双引号“”(让引号内的变量生效)
单引号‘’(引号内的变量当作一个字符使用)
反斜线\(禁用特殊字符功能\$)
后引号``(让引号内的命令生效)
单引号和双引号的区别在于:单引号是原样显示,双引号则显示出变量的值。
echo time is:
'date'
abc="100"
echo 'time is: $abc'
echo "time is: $abc"
有时候变量名很容易与其他文字混淆,比如:
num=2
echo "this is the $numnd"
这并不会打印出"this is the 2nd",而仅仅打印"this is the ",因为shell会去搜索变量numnd的值,但是这个变量时没有值的。
可以使用花括号(或中括号)来告诉shell我们要打印的是num变量:
num=2
echo "this is the ${num}nd" //或者echo "this is the $[num]nd"
这将打印:this is the 2nd
流程控制
1) if
"if" 表达式如果条件为真则执行then后面的部分:
if 条件1 #如果条件1为真
then #那么
command1 #执行命令1
elif 条件2 # 如果条件2为真
then #那么
command2 # 执行命令2
else #如果条件1和条件2都不成立
command3 执行命令3
fi #完成(if 语句一定要以fi结束)大多数情况下,可以使用测试命令来对条件进行测试。
比如可以比较字符串、判断文件是否存在及是否可读等等…
通常用" [ ] "来表示条件测试。
注意这里的空格很重要。
要确保方括号的空格。
[ -f "somefile" ] :判断是否是一个文件
[ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限
[ -n "$var" ] :判断$var变量是否有值
[ "$a" = "$b" ] :判断$a和$b是否相等
例如:
//变量$SHELL包含了登录shell的名称,我们和/bin/bash进行了比较
if [ "$SHELL" = "/bin/bash" ]; then
echo "your login shell is the bash (bourne again shell)"
else
echo "your login shell is not bash but $SHELL"
fi
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwors"
这里&& 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句。
您也可以认为是逻辑运算中的与操作。
上例中表示如果/etc/shadow文件存在则打印” This computer uses shadow passwors”。
2) case
case :表达式可以用来匹配一个给定的字符串,而不是数字。
case 值in
模式1)#如果模式1匹配
命令1 #执行命令1
;;
模式2)#如果模式2匹配
命令2 #执行命令2
;;
*)#如果以上模式都不匹配
命令3 #执行命令3
;;
esac #结束case语句
case取值后面必须为单词in,每一模式必须以右括号结束。
取值可以为变量或常数。
匹配发现取值符合某一模式后,其间所有命令开始执行直至;;。
模式匹配符号*表示匹配任意字符。
[。
]表示类或者范围中的任意字符。
让我们看一个例子。
file命令可以辨别出一个给定文件的文件类型,比如:
file test.gz
这将返回:
test.gz: gzip compressed data, deflated, original filename,
last modified: Mon Aug 27 23:09:18 2001, os: Unix
我们利用这一点写了一个叫做smartzip的脚本,该脚本可以自动解压bzip2, gzip 和zip 类型的压缩文件:#!/bin/sh
ftype=`file "$1"`
case "$ftype" in
"$1: Zip archive"*)
unzip "$1" ;;
"$1: gzip compressed"*)
gunzip "$1" ;;
"$1: bzip2 compressed"*)
bunzip2 "$1" ;;
*) echo "File $1 can not be uncompressed with smartzip";;
esac
您可能注意到我们在这里使用了一个特殊的变量$1。
该变量包含了传递给该程序的第一个参数值。
也就是说,当我们运行:
smartzip articles.zip
$1 就是字符串articles.zip
3) selsect
select 表达式是一种bash的扩展应用,尤其擅长于交互式使用。
用户可以从一组不同的值中进行选择。
select var in ... ; do
break
done
.... now $var can be used ....
下面是一个例子:
#!/bin/sh
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
break
done
echo "You have selected $var"
下面是该脚本运行的结果:
What is your favourite OS?
1) Linux
2) Gnu Hurd
3) Free BSD
4) Other
#? 1
You have selected Linux
4)for循环
for 变量名in 列表
do
命令1
命令2
done
当变量值在列表里,for 循环即执行一次所有命令,使用变量名访问列表取值。
命令可为任何有效的shell命令和语句。
变量名为任何单词。
in列表用法是可选的,如果不用它,for循环使用命令行的位置参数。
in列表可以包含替换,字符串和文件名
举例说明:
for loop in 1 2 3 4 #在1 2 3 4列表中循环(4次)
do #开始执行命令
echo "hello" #执行命令
done #完成
输出结果:
hello
hello
hello
hello
for loop in "one two three" #当成一个元素只循环一次
do
echo $loop
done
#输出one two three 一行
for loop in one two three #三次循环
do
echo $loop
done
#输出
one
列表也可以是文件,如:for loop in `cat myfile`
5) while循环
while 命令
do
命令1
命令2
......
done
举例说明:
while read LINE <myfile #每次读取myfile 中的一行
do
echo $LINE #输出每行的信息
done
getopts用法
获取多个命令行参数。
举例说明:
#!/bin/bash
#getopts
ALL=false
HELP=false
FILE=false
VERBOSE=false
while getopts ahfvc: OPTION #将ahfvc依次传给OPTION 。
c后面的冒号:表示-c时需传入参数do
case ${OPTION} in
a)
ALL=true
echo "ALL IS ${ALL}"
;;
h)
HELP=true
echo "HELP IS ${HELP}"
;;
f)
FILE=true
echo "FILE IS ${FILE}"
;;
v)
VERBOS=false
echo "VERBOSE IS ${VERBOSE}"
c=${OPTARG}
echo "c value is $c"
;;
\?)
echo "`basename $0` -[a h f v] -[c value] file"
;;
esac
done
运行结果:
输入./getopts -a 输出:ALL IS true #执行case a模式的命令
输入./getopts -h 输出:HLEP IS true #执行case h模式的命令
输入./getopts -f 输出:FILE IS true #执行case f模式的命令
输入./getopts -v 输出:VERBOSE IS true #执行case v模式的命令
输入./getopts -c 提示错误:需要传入参数#c后面有“:”所以需传参数
输入./getopts -c hello 输出:c value is hello #执行case c模式的命令
输入./getopts -b 输出:basename ./getopts -[a h f v] -[c value] file #其他情况
awk用法
#!/bin/awk -f #注明是awk的语法,若无此行则按bash的语法编译会出错
#awk_array.sh
BEGIN{ #BEGIN模式中的命令
FS="#"
score["0-60"]=0 #score数组索引为"0-60"的元素(awk语法允许用字符串索引)score["60-70"]=0
score["70-80"]=0
score["80-90"]=0
score["90-100"]=0
student["junior"]=0
student["senior"]=0
}
{
{ if ($1<60) #如果第一个域的值小于60
score["0-60"]++ #score数组中索引为"0-60"的元素值+1
}
{ if($1>=60 && $1<70)
score["60-70"]++
}
{ if($1>=70 && $1<80)
score["70-80"]++
}
{ if($1>=80 && $1<90)
score["80-90"]++
}
{ if($1>=90&&$1<=100)
score["90-100"]++
}
}
{ #另senior_junior依次为student数组中的索引(有几个就循环几次)
for (senior_junior in student)
{if ($2==s enior_junior)
student[senior_junior]++
}
}
END{
{ for (number in score) print "the score",number,"has",score[number],"students"}
{ for (senior_junior in student) print "The class has ",student[senior_junior],senior_junior,"students" } }
若有文件grade.txt如下:
85#senior
87#junior
78#junior
69#senior
56#junior
98#senior
83#senior
输入命令./awk.sh grade.txt 则输出如下:
the score 0-60 has 1 students
the score 70-80 has 1 students
the score 90-100 has 1 students
the score 60-70 has 1 students
the score 80-90 has 3 students
The class has 4 senior students
The class has 3 junior students
以时间为标识的日志文件。
适用于长期存储的日志。
举个创建以时间为标识日志的例子入下:
#!/bin/bash
#datelog.sh
current_date=`date ""+%Y%m%d` #当前的日期(年月日)
todaylog="/tmp/${current_date}.log" #今天日志的(绝对路径)文件名。
也即该文件在/tmp目录下#如果日志文件不存在,创建一个
if [ ! -f $todaylog ]
then
touch $todaylog
fi
#输出日志到日志文件
log_time_format=`date "+%Y-%m-%d %T"`
echo "${log_time_format} commands start.....">>${todaylog}
# commands blocks。
例如这里将errpt报错及df –g命令结果输出到${todaylog}所示的日志文档
errpt>>${todaylog}
df -g>>$todaylog // $todaylog与${todaylog}相同
sleep 4
log_time_format=`date "+%Y-%m-%d %T"`
echo "${log_time_format} commands end!">>${todaylog} #结束时记录日志
(标准输入)<<
举例来说明:
#!/bin/bash
#input.sh
cat<<hello #打印此处开始到hello结束之间的所有字符
***************************
This is a test!
***************************
hello #标准输入到此行之前结束(注意:这个hello必须在句首,其前面不能有任何字符,甚至是空格)则输出:
****************************
This is a test!
****************************
trap捕捉信号
(1)信号可以被应用程序或脚本捕获,并依据该信号(1、2、3和15)采取相应的行动。
一些信号不能被捕获。
如,如果一个命令收到了信号9,就无法再捕获其他信号。
(2)捕捉到一个信号后,可能会采取三中
1].不采取任何行动,由系统来进行处理(如信号9由系统处理)
2].捕获该信号,但忽略它
3].捕获该信号,并采取相应的行动
(3)trap可以使你在脚本中捕捉信号,命令形式为trap name signal(s)
其中,name是捕捉到信号以后所采取的一系列操作。
实际中,name一般是一个专门用来处理所捕捉信号的函数。
name需要用双引号(“”)引起来。
signal是待捕捉的信号。
最常见的行动包括:
1]清除临时文件
2]忽略该信号(如trap "" 23)
3]询问用户是否终止该脚本进程
(4)举例说明:
#!/bin/bash
#trap.sh
trap “exitprocess” 2#捕捉到信号2之后执行exitprocess function
LOOP=0
function exitprocess()
{
echo "You Just hit <CTRL-C>, at number $LOOP"
echo "I will now exit"
exit 1
}
while : # 循环直到捕捉到信号(注意中间的空格)
do
LOOP=$[ $LOOP+1 ]
echo $LOOP
sleep 1
done
有好多方法可以实现对输入参数的分析,但是下面的使用case表达式的例子无遗是一个不错的方法。
#!/bin/sh
# cmdparser.sh
help()
{
cat <HELP
This is a generic command line parser demo.
USAGE EXAMPLE: ./cmdparser.sh -l hello -f -- -somefile1 somefile2
HELP
exit 0 #exit 0表示本函数执行到此,shell程序就会退出,不再执行下面的语句。
如果这里没exit 0,则本函数执行完后,继续执行下面的语句。
}
while [ -n "$1" ]; do
case $1 in
-h) help;shift 1;; # function help is called
-f) opt_f=2;shift 1;; # variable opt_f is set
-l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2
--) shift;break;; # end of options
-*) echo "error: no such option $1. -h for help";exit 1;;
*) break;;
esac
done
echo "opt_f is $opt_f"
echo "opt_l is $opt_l"
echo "first arg is $1"
echo "second arg is $2"
您可以这样运行该脚本:
cmdparser -l hello -f -- -somefile1 somefile2
返回的结果是:
opt_f is 2
opt_l is hello
first arg is -somefile1
second arg is somefile2
这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。
根据unix系统的惯例,首先输入的应该是包含减号的参数.
设计一个Shell程序,在/userdata目录下建立50个目录,即user1~user50,并设置每个目录的权限为rwxr-xr—
方法一:
#!/bin/bash
#最简单,效率最高的办法
mkdir -p /userdata/{1..50} && chmod 754 /userdata/{1..50}
方法二:
用for或while循环
#!/bin/bash
i=0
while [ $i -lt 50 ];do
let i=i+1
mkdir -p /userdata/$i
chmod 754 /userdata/$i
done
方法三:
#!/bin/bash
for D in /userdata/{1..20}
do
mkdir -m 754 -p $D
done
echo "Completed"
系统中有个文件,文件名为ABC.txt。
如何将当前的系统时间追加到此文件行首?
方法一:
echo -e "`date`\n`cat ABC.txt`" > ABC.txt //不晓得-e是什么意思,但如果没有-e,则追加到行首文本不会
换行
方法二:
echo "`date | cat - ABC.txt`" > ABC.txt
关于$* 与$@ 的说明
$* 以单变量的形式显示所有的参数列表,由环境变量IFS中的第一个字符分隔.
$@ $*的一个灵巧变形.他并不使用IFS环境变量,所以如果IFS为空那么所有的所有的参数会一起运行. 我们可以通过下面的测试容易的看出$@和$*的区别:
$ IFS=’’
$ set foo bar bam
$ echo “$@”
foo bar bam
$ echo “$*”
foobarbam
$ unset IFS
$ echo “$*”
foo bar bam
正如我们所看到的,在双引号内,$@将参数进行分隔显示,而与IFS的值无关.通常来说,如果我们要访问参数,$@是一个很灵敏的选择.
Shell变量
主要有本地变量和环境变量。
1、本地变量--在用户现有运行的脚本中使用
1) 定义本地变量格式:variable-name=value
例子:[root@jike1 /root]# LOCALTEST="test"
[root@jike1 /root]# echo $LOCALTEST (注意:echo $LOCALTEST 和echo ${LOCALTEST}的效果是一样的)
(在变量名前加$, 可以取得此变量的值,使用echo命令可以显示变量的值)
2) 显示本地变量格式:set
例子:[root@chinaitlab root]# set
3) 清除本地变量格式:unset variable-name
例如:[root@jike1 /root]# unset LOCALTEST
此时再执行echo $LOCALTEST将看不到变量LOCALTEST的输出。
2、环境变量--在所有的子进程中使用
1) 定义环境变量格式:export variable-name=value (与本地变量的定义相比,多了一个export 关键字)
例子:[root@chinaitlab /root]# export DOMAIN=""
[root@ chinaitlab shell]# vi testenv.sh
#!/bin/bash #表示用bash来解析脚本
#testenv.sh
echo $DOMAIN
[root@chinaitlab shell]# chmod +x testenv.sh
[root@chinaitlab shell]# ./testenv.sh
2) 显示环境变量格式:env (本地变量的显示使用set,环境变量的显示使用env)
例子:[root@chinaitlab test]# env
3) 清除环境变量格式:unset variable-name (用法与本地变量相同,都使用unset)
例子:[root@chinaitlab shell]# unset DOMAIN
此时再执行./testenv.sh将看不到变量DOMAIN的输出。
3、其它变量
1) 位置变量$0,$1,$2,$3……$9
2) 只读变量readonly variable
注意:只读变量不能被清除和改变其值,所以要谨慎使用。
3) 特殊变量$#,$?,$$(表示当前进程的PID)……
cat命令的一点用法
1. 创建一个包含myfile1、myfile2、myfile3三个文件内容,名为b i g f i l e的文件:
cat myfile1 myfile2 myfile3 > bigfile
2. 用cat新建文件
$cat >myfile
Hello
This is great
<ctrl-d>
$cat myfile
Hello
This is great
3. 显示myile文件,并带有行号
cat –n myfile
把myfile 的内容加上行号后输入myfile1这个文件里
cat -n myfile > myfile1
显示myfile文件的内容,同时拷贝文件为file1、file2、file3。
cat myfile | tee file1 file2 file3 //tee命令:读取标准输入的数据,并将其内容输出成文件。