shell编程基础

目前Linux 系統下最流行的运维自动化语言就是Shell和Python. 而Shell更是在服务监控、业务快速部署、服务启动停止、数据备份及处理、日志分析等环节必不可少。Python与shell 是互补的,Shell更适合系统底层,Python则更适合处理复杂的业务逻辑,以及开发复杂的运维软件工具等。

什么是Shell

Shell是一个命令解释器,它的作用是解释执行用户输入的命令及程序等,存在于操作系统最外层,负责把用户的输入解释给操作系统并处理各种各样的输出结果,然后输出到屏幕返回给用户。这是一种交互的方式。
当命令或程序不在命令行下执行,而是通过一个程序文件来执行时,该程序就被称为Shell脚本。里面内置了很多条命令、语句及循环控制,是一种非交互式方式。

shell脚本基本规范

1)通常在第一行指出由哪个程序(解释器)来执行脚本中的内容。如:

#!/bin/bash#!/bash/sh #<==255个字符以内。

注:这一行必须位于第一行,若不是则为脚本注释行。

2)脚本开头加版本、版权信息:

#Date:  2017-08-24 15:03:56
#Author:    Create by ts1130
#Description: This script's function is ...
#Version: 1.1

3)脚本中尽量不用中文
4)命名以.sh为拓展名
5)脚本存放到固定路径下
如:/serser/scripts
6)对于常规的字符串定义变量值应加双引号,并且等号后不能有空格,需要强调引用的,用单引号(’ ‘),如果是命令的引用,则用反引号(直接按下esc下面的键打出)。

Shell 脚本的执行

当Shell脚本运行时,它会先查找系统环境变量ENV,该变量指定了环境文件,该变量指定了环境文件(加载顺序通常是/etc/profile、~/.bash_profile、~/.bashrc、etc/bashrc等)然后再开始执行脚本中的命令。

执行方式

1)bash script-name 或 sh script-name:适合脚本文件本身没有可执行权限时使用,推荐用这种方法。
2)path/script-name 或 ./script-name:指在当前路径下执行脚本,但需要脚本有可执行权限。此方法常因为忘记给脚本添加可执行权限(chmod+n script-name)而无法执行。
3 ) source script-name 或. script-name(“.”与后面的脚本名之间有空格):使用source 或“.”可以将自身脚本中的变量值或函数等的返回值传递到前父Shell脚本中使用,这些脚本将在父Shell脚本中运行(其他几种模式都会启动新的进程执行子脚本)。source或“.”相当于include的功能,此方法同样可以执行无可执行权限的脚本。而且脚本执行完后会保留脚本中的函数与变量。
4)sh< script-name或cat script-name|sh:同样适合用于bash,不过不常见,如不用循环语句来实现精简开机自启动服务的案例,通过将所有字符串拼接为命令的形式,经由管道交给bash操作系统。

Shell中的特殊变量

特殊位置变量

1)#n:获取当前执行脚本的第n个参数值, n = 1..9, 当n为0时表示脚本的文件名(包括路径);如果n大于9,则用大括号括起来,如${10}

当n为0时,若执行脚本为全路径,可以分别用dirname 与 basename 获取脚本的路径与名字
2) $# :获取当前执行脚本后面接的参数的总个数

#文件a.sh中:
echo $1 $2 $3 $4 $5 $6 $7 $8 $9
echo $#
#执行:sh a.sh {a..z}
输出:
a b c d e f g h i
26

3)$* 与 $@ :取当前脚本所有传参的参数(不加引号)

"$*":表示将所有的参数视为单个字符串,相当于"$1 $2"
"$@":表示将所有的参数视为不同的独立的字符串,相当于"$1" "$2" "$3"

特殊状态变量

1)$?:获取上条指令的执行返回值(0为成功,非零为失败)

2)$$:获取当前执行的脚本的进程号PID

bash Shell内置变量命令

用户在命令行输入命令后,一般情况下Shell会fork并exec该命令,但是Shell的内建命令例外,执行内建命令相当于调用Shell进程中的一个函数,并不创建新的进程。以前学过的cd、alias、umask、exit等命令即是内建命令,凡是用which命令查不到程序文件所在位置的命令都是内建命令,内建命令没有单独的man手册,要在man手册中查看内建命令,应该执行man bash-builtins
如export、shift、if、eval、[、for、while等等。内建命令虽然不创建新的进程,但也会有Exit Status,通常也用0表示成功非零表示失败,虽然内建命令不创建新的进程,但执行结束后也会有一个状态码,也可以用特殊变量$?读出。

Shell基本语法

变量

按照惯例,Shell变量通常由字母加下划线开头,由任意长度的字母、数字、下划线组成。有两种类型的Shell变量:

1. 环境变量

环境变量可以从父进程传给子进程,因此Shell进程的环境变量可以从当前Shell进程传给fork出来的子进程。用printenv命令可以显示当前Shell进程的环境变量。Shell通过环境变量(一般通过export内置命令导出)来确定登陆用户名、命令路径、终端类型、登陆目录等

2. 本地变量

只存在于当前Shell进程,用set命令可以显示当前Shell进程中定义的所有变量(包括本地变量和环境变量)和函数。
环境变量是任何进程都有的概念,而本地变量是Shell特有的概念。在Shell中,环境变量和本地变量的定义和用法相似。在Shell中定义或赋值一个变量:VARNAME=value
注意等号两边都不能有空格,否则会被Shell解释成命令和命令行参数。
一个变量定义后仅存在于当前Shell进程,它是本地变量,用export命令可以把本地变量导出为环境变量,定义和导出环境变量通常可以一步完成:export VARNAME=value
也可以分两步完成:
VARNAME=value
export VARNAME
用unset命令可以删除已定义的环境变量或本地变量。
unset VARNAME

文件名代换(Globbing):* ? []

这些用于匹配的字符称为通配符(Wildcard),如:* ? [ ] 具体如下:
* 匹配0个或多个任意字符
? 匹配一个任意字符
[若干字符] 匹配方括号中任意一个字符的一次出现

注:Globbing所匹配的文件名是由Shell展开的,也就是说在参数还没传给程序之前已经展开了,比如上述ls ch0[012].doc命令,如果当前目录下有ch00.doc和ch02.doc,则传给ls命令的参数实际上是这两个文件名,而不是一个匹配字符串。

命令代换:`或 $()

由“`”反引号括起来的也是一条命令,Shell先执行该命令,然后将输出结果立刻代换到当前命令行中。例如定义一个变量存放date命令的输出:

DATE=`date` 
echo $DATE 

命令代换也可以用$()表示:

DATE=$(date)

算术代换:$(())、$[]

使用$(()),用于算术计算,(())中的Shell变量取值将转换成整数,同样含义的$[ ]等价例如:

VAR=45
echo $(($VAR+3))   等价于 echo $((VAR+3)) 或 echo $[$VAR+3] 或 echo $[VAR+3]

注:$(())中只能用+-*/和()运算符,并且只能做整数运算。
$[base#n],其中base表示进制,n按照base进制解释,后面再有运算数,按十进制解释。

echo $[2#10+11]        -->13
echo $[8#10+11]        -->19
echo $[16#10+11]       -->27

转义字符:\

和C语言类似,\在Shell中被用作转义字符,用于去除紧跟其后的单个字符的特殊意义(回车除外),换句话说,紧跟其后的字符取字面值。例如:

echo $SHELL
/bin/bash
echo \$SHELL
$SHELL
echo \\
\

比如创建一个文件名为“$ $”的文件($间含有空格)可以这样:

touch \$\ \$

还有一个字符虽然不具有特殊含义,但是要用它做文件名也很麻烦,就是-号。如果要创建一个文件名以-号开头的文件,可以有两种办法:

touch ./-hello
或者
touch -- -hello

这样是不正确的:

touch -hello
touch: invalid option -- h
Try `touch --help' for more information.

即使加上\转义也还是报错:

touch \-hello
touch: invalid option -- h
Try `touch --help' for more information.

因为各种UNIX命令都把-号开头的命令行参数当作命令的选项,而不会当作文件名。

\还有一种用法,在\后敲回车表示续行,Shell并不会立刻执行命令,而是把光标移到下一行,给出一个续行提示符>,等待用户继续输入,最后把所有的续行接到一起当作一个命令执行。例如:

ls \
> -l

(ls -l命令的输出)

单引号与双引号

和C语言相同,Shell脚本中的单引号和双引号一样都是字符串的界定符,而不是字符的界定符。
单引号用于保持引号内所有字符的字面值,即使引号内的\和回车也不例外,但是字符串中不能出现单引号。如果引号没有配对就输入回车,Shell会给出续行提示符,要求用户把引号配上对。例如:

echo '$SHELL'
$SHELL
echo 'ABC\(回车)
> DE'(再按一次回车结束命令)
ABC\
DE

被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引号的处理方式不同

DATE=$(date)
echo "$DATE"
echo '$DATE'

再比如:

VAR=200
echo $VAR  
200
echo '$VAR'
$VAR
echo "$VAR"
200

Shell脚本语法

条件测试:test [

命令test或[可以测试一个条件是否成立,如果测试结果为真,则该命令的Exit Status为0,如果测试结果为假,则命令的Exit Status为1(注意与C语言的逻辑表示正好相反)。例如测试两个数的大小关系:

itcast@ubuntu:~$ var=2
itcast@ubuntu:~$ test $var -gt 1
itcast@ubuntu:~$ echo $?
0
itcast@ubuntu:~$ test $var -gt 3
itcast@ubuntu:~$ echo $?
1
itcast@ubuntu:~$ [ $var -gt 3 ]
itcast@ubuntu:~$ echo $?
1
itcast@ubuntu:~$

虽然看起来很奇怪,但左方括号[确实是一个命令的名字,传给命令的各参数之间应该用空格隔开,比如,$VAR、-gt、3、]是[命令的四个参数,它们之间必须用空格隔开。命令test或[的参数形式是相同的,只不过test命令不需要]参数。以[命令为例,常见的测试命令如下表所示:

[ -d DIR ]              如果DIR存在并且是一个目录则为真
[ -f FILE ]             如果FILE存在且是一个普通文件则为真
[ -z STRING ]           如果STRING的长度为零则为真
[ -n STRING ]           如果STRING的长度非零则为真
[ STRING1 = STRING2 ]   如果两个字符串相同则为真
[ STRING1 != STRING2 ]  如果字符串不相同则为真
[ ARG1 OP ARG2 ]        ARG1ARG2应该是整数或者取值为整数的变量,OP-eq(等于)-ne(不等于)-lt(小于)-le(小于等于)-gt(大于)-ge(大于等于)之中的一个

和C语言类似,测试条件之间还可以做与、或、非逻辑运算:

[ ! EXPR ]          EXPR可以是上表中的任意一种测试条件,!表示逻辑反
[ EXPR1 -a EXPR2 ]  EXPR1EXPR2可以是上表中的任意一种测试条件,-a表示逻辑与
[ EXPR1 -o EXPR2 ]  EXPR1EXPR2可以是上表中的任意一种测试条件,-o表示逻辑或

例如:

$ VAR=abc
$ [ -d Desktop -a $VAR = 'abc' ]
$ echo $?
0

注意,如果上例中的$VAR变量事先没有定义,则被Shell展开为空字符串,会造成测试条件的语法错误(展开为[ -d Desktop -a = ‘abc’ ]),作为一种好的Shell编程习惯,应该总是把变量取值放在双引号之中(展开为[ -d Desktop -a “” = ‘abc’ ]):

$ unset VAR
$ [ -d Desktop -a $VAR = 'abc' ]
bash: [: too many arguments
$ [ -d Desktop -a "$VAR" = 'abc' ]
$ echo $?
1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值