bash是linux学习中重要的一环,他可以批量处理linux命令,完成多个单任务的组合。提高运维效率!
bash编程语法一:变量
bash变量类别:
本地变量:只对当前shell进程有效的变量;对其它shell进程无效,包当前shell进程的子进程;
VAR_NAME=VALUE
变量赋值:向变量的存储空间保存数据
变量引用:${VAR_NAME}
"":弱引用,里面的变量会被替换;
'':强引用,里面的所有字符都是字面量,直接输出;
[root@www ~]# varname=aolens.com
[root@www ~]# echo ${aolens.com}
-bash: ${aolens.com}: bad substitution
[root@www ~]# echo ${varname}
aolens.com
[root@www ~]# echo "$varname"
aolens.com
[root@www ~]# echo '$varname'
$varname
环境变量:对当前shell进程及其子shell有效,对其它的shell进程无效;
定义:export VAR_NAME=VALUE
导出:export VAR_NAME
使用env命令显示所有的环境变量
撤消变量:unset VAR_NAME
只读变量:readonly VAR_NAME
局部变量:对shell脚本中某代码片断有效;通常用于函数本地;
local VAR_NAME=VALUE
位置变量:用来接受变量指定位置的参数。
$1(第一个参数), $2(第二个参数), ..., ${10}
$0(代表变量本身)
特殊变量:
$# |
位置参数的个数 |
"$*" |
代表"$1 $2 $3 $4..."空格间隔 |
"$@" |
所有的位置参数(每个都独立“$1” "$2" "$3" ...) |
${#*} |
传递到脚本中的命令行参数的个数 |
${#@} |
传递到脚本中的命令行参数的个数 |
$? |
返回值 |
$$ |
脚本的进程ID(PID) |
$! | 运行在后台的最后一个作业的进程ID(PID) |
set可以设置某个环境变量的值。清除环境变量的值用unset命令。如果未指定值,则该变量值将被设为NULL。
变量命名:
1、不能使用程序中的关键字(保留字);
2、只能使用数字、字母和下划线,且不能以数字开头;
3、要见名知义
变量类型:
精确数值:整数
近似数值:浮点型
字符型:char,string
布尔型:true, false
bash的配置文件:
profile类:为交互式登录的用户提供配置
全局:/etc/profile、/etc/profile.d/*.sh
用户:~/.bash_profile
bashrc类:为非交互式的用户提供配置
全局:/etc/bashrc
用户:~/.bashrc
bash编程:解释器
shell脚本:
第一行,顶格:
#!/bin/bash-->用来识别是否bash脚本
#!/usr/bin/python
其它的以#开头的行均为注释,会被解释器忽略;可作为脚本内命令说明。
bash编程:测试脚本
-n: 语法测试,测试是否存在语法错误
-x: 模拟单步执行,将每一步的执行过程结果都显示出来。
bash编程:for循环语句
for第一种格式:
for 变量名 in 列表; do
循环体
done
循环体:依赖于调用变量来实现其变化;
循环可以嵌套;
退出条件:遍历列表完成
for i in {1..10};do
echo $i
done
for的第二种使用格式 :
for ((初始条件;测试条件;修改表达式)); do
循环体
done
先用初始条件和测试条件做判断,如果符合测试条件则执行循环体,再修改表达式。否则直接跳出循环。
for((i=0;i<10;i++));do
echo $i
done
bash编程:while、until循环语句
while格式:
while 测试条件; do
循环体
done
read -p "enter num" num
while [ $num -gt 10 ];do
echo ‘num>10’
read -p "enter num" num
done
测试条件为真,进入循环;测试条件为假,退出循环;
测试条件一般通过变量来描述,需要在循环体不变量地改变变量的值,以确保某一时刻测试条件为假,进而结束循环;
until格式:
until 测试条件; do
循环体
done
测试条件为假,进入循环;测试条件为真,退出循环;
测试条件一般通过变量来描述,需要在循环体不变量地改变变量的值,以确保某一时刻测试条件为真,进而结束循环;
bash编程:交互式编程
read [option] “prompt”
-p:直接指定一个变量接受参数
-t timaout:指定等待接受参数的时间
-n:表示不换行
例如:输入用户名,可返回其shell
#!/bin/bash
read -p "Plz input a username: " userName
if id $userName &> /dev/null; then
echo "The shell of $userName is `grep "^$userName\>" /etc/passwd | cut -d: -f7`."
else
echo "No such user. stupid."
fi
bash编程:逻辑运算
&&(与): condition1 && condition2 == [ condition1 -a condition2 ]
||(或): condition1 || condition2 == [ condition1 -o condition2 ]
!(非):! condition == [ -not condition ]
bash编程:if选择
if: 三种使用格式
(1)单分支的if语句:表示条件测试状态返回值为值,则执行选择分支;
if 测试条件; then
选择分支
fi
(2)双分支的if语句:两个分支仅执行其中之一
if 测试条件; then
选择分支1
else
选择分支2
fi
练习:通过命令行给定一个文件路径,而后判断:
如果此文件中存在空白行,则显示其空白行的总数;
否则,则显示无空白行;
if grep "^[[:space]]*$" $1 &> /dev/null; then
echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
else
echo "No blank lines"
fi
注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。
(3)多分支的if语句:
if 条件1; then
分支1
elif 条件2; then
分支2
elif 条件3; then
分支3
...
else
分支n
fi
练习:传递一个用户名给脚本:
如果此用户的id号为0,则显示说这是管理员
如果此用户的id号大于等于500,则显示说这是普通用户
否则,则说这是系统用户;
#!/bin/bash
#
if [ $# -lt 1 ]; then
echo "Usage: `basename $0` username"
exit 1
fi
if ! id -u $1 &> /dev/null; then
echo "Usage: `basename $0` username"
echo "No this user $1."
exit 2
fi
if [ $(id -u $1) -eq 0 ]; then
echo "Admin"
elif [ $(id -u $1) -ge 500 ]; then
echo "Common user."
else
echo "System user."
fi
bash编程:case选择
case语句:有多个测试条件时,case语句会使得语法结构更明晰
case格式:
case 变量引用 in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
...
*)
分支n
;;
esac
PATTERN:类同于文件名通配机制,但支持使用|表示或者;
a|b: a或者b
*:匹配任意长度的任意字符
?: 匹配任意单个字符
[]: 指定范围内的任意单个字符
写一个脚本,使用格式:
script.sh {start|stop|restart|status}
1) start: 创建/var/lock/subsys/script.sh
2) stop: 删除此文件
3) restart: 先删除文件,再创建文件
4) status: 如文件存在,显示running,否则,显示stopped
#!/bin/bash
srv=`basename $0`
lockFile="/var/lock/subsys/$srv"
[ $# -lt 1 ] && echo "Usage: $srv {start|stop|restart|status}" && exit 4
[ $UID -ne 0 ] && echo "Only root." && exit 5
if [ "$1" == 'start' ]; then
if [ -f $lockFile ]; then
echo "$srv is running."
exit 7
else
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srv OK."
fi
elif [ "$1" == 'stop' ]; then
if [ -f $lockFile ]; then
rm -f $lockFile
[ $? -eq 0 ] && echo "Stopping $srv OK."
else
echo "$srv is stopped yes."
exit 6
fi
elif [ "$1" == 'restart' ]; then
if [ -f $lockFile ];then
rm -f $lockFile
[ $? -eq 0 ] && echo "Stopping $srv OK."
else
echo "$srv is stopped yet."
fi
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srv OK."
elif [ "$1" == 'status' ];then
if [ -f $lockFile ];then
echo "$srv is running."
else
echo "$srv is stopped."
fi
else
echo "Usage: $srv {start|stop|restart|status}" && exit 9
fi
bash编程:算术运算
bash会对数字执行隐式的类型转换
declare -i Var_Name=Integer_Value:定义一个整型变量
let Var_Name=EXPRESSION
$[EXPRESSION]
$((EXPRESSION))
命令:expr ARG1 OP ARG2
操作符:
+, -, *, /, %(取模), **(次幂)
双目运算符:需要至少两个操作数
bash编程:条件测试
命令执行成功与否即为条件测试
测试类型:根据比较时的操作数的类型
整型测试:整数比较
字符测试:字符串比较
文件测试:判断文件的存在性及属性等
(1)整型测试:
例如 [ $num1 -gt $num2 ]
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
-eq | 等于 |
-ne | 不等于 |
-n string(单目测试) |
是否不空,不空则为真,空则为假 |
-z string (单目测试) |
是否为空,空则为真,不空则假 |
(2)字符串测试:
例如:[[ "$str1" > "$str2" ]]
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
(3)文件测试:
-a file | 存在则为真;否则则为假 |
-e file | 存在则为真;否则则为假 |
-f file | 存在并且为普通文件,则为真;否则为假 |
-d file | 存在并且为目录文件,则为真;否则为假 |
-L/-h file | 存在并且为符号链接文件,则为真;否则为假 |
-b |
存在并且为块设备,则为真;否则为假 |
-c |
存在并且为字符设备,则为真;否则为假 |
-S |
存在并且为套接字文件,则为真;否则为假 |
-s |
存在并且飞空,则为真;否则为假 |
-p |
存在并且为命名管道,则为真;否则为假 |
-r |
文件可读为真,否则为假 |
-w |
文件可写为真,否则为假 |
-x |
文件可执行为真,否则为假 |
bash编程:函数
语法:
function F_NAME {
函数体
}
F_NAME() {
函数体
}
可调用:使用函数名
函数名出现的地方,会被自动替换为函数;
脚本:
函数的返回值:
函数的执行结果返回值:代码的输出
函数中的打印语句:echo, print
函数中调用的系统命令执行后返回的结果
执行状态返回值:
函数体中最后一次执行的命令状态结果
自定函数执行状态的返回值:return #
函数可以接受参数:
在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:位置参数
$1, $2, ...
$#, $*, $@
示例:服务脚本示例
#!/bin/bash
#
# chkconfig: 2345 67 34
#
srvName=$(basename $0)
lockFile=/var/lock/subsys/$srvName
start() {
if [ -f $lockFile ];then
echo "$srvName is already running."
return 1
else
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srvName OK."
return 0
fi
}
stop() {
if [ -f $lockFile ];then
rm -f $lockFile &> /dev/null
[ $? -eq 0 ] && echo "Stop $srvName OK" && return 0
else
echo "$srvName is not started."
return 1
fi
}
status() {
if [ -f $lockFile ]; then
echo "$srvName is running."
else
echo "$srvName is stopped."
fi
return 0
}
usage() {
echo "Usage: $srvName {start|stop|restart|status}"
return 0
}
case $1 in
start)
start
;;
stop)
stop ;;
restart)
stop
start ;;
status)
status ;;
*)
usage
exit 1 ;;
esac
bash编程:信号捕捉
trap 'COMMAND' SIGNALE
第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双
引号中的命令串。
trap 'commands' SININT(表示关闭进程)
trap "commands" SIGINT(表示关闭进程)
脚本:写一个脚本,能够ping探测指定网络内的所有主机是否在线?当没有执行完时可接收ctrl+c命令退出!
#!/bin/bash
#
quitScript() {
echo "Quit..."
}
trap 'quitScript; exit 5' SIGINT
cnetPing() {
for i in {1..254}; do
if ping -c 1 -W 1 $1.$i &> /dev/null; then
echo "$1.$i is up."
else
echo "$1.$i is down."
fi
done
}
bnetPing() {
for j in {0..255}; do
cnetPing $1.$j
done
}
anetPing() {
for m in {0..255}; do
bnetPing $1.$m
done
}
netType=`echo $1 | cut -d"." -f1`
if [ $netType -ge 1 -a $netType -le 126 ]; then
anetPing $netType
elif [ $netType -ge 128 -a $netType -le 191 ]; then
bnetPing $(echo $1 | cut -d'.' -f1,2)
elif [ $netType -ge 192 -a $netType -le 223 ]; then
cnetPing $(echo $1 | cut -d'.' -f1-3)
else
echo "Wrong"
exit 2
fi
bash编程:shift
如果没有数字,只有shift 就是跳过一个参数获取下一个参数,如果加上数字,比如shift 2 ,跳过两个参数获取下一个参数
比如:
$1=1 $2=2 $3=3
那么shift后
$1=2 $2=3 $3=""
如果shift 2后
$1=3 $2="" $3=""
练习:写一个脚本,使用形式如下所示
showifinfo.sh [-i INTERFACE|-a] [-v]
要求:
1、-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;
显示接口的ip地址
2、使用-v,则表示显示详细信息
显示接口的ip地址、子网掩码、广播地址;
3、默认表示仅使用-a选项;
#!/bin/bash
#
verbose=0
allInterface=0
ifflag=0
interface=0
while [ $# -ge 1 ]; do
case $1 in
-a)
allInterface=1
shift 1
;;
-i)
ifflag=1
interface="$2"
shift 2
;;
-v)
verbose=1
shift
;;
*)
echo "wrong option"
exit 2
;;
esac
done
if [ $allInterface -eq 1 ]; then
if [ $verbose -eq 1 ]; then
ifconfig | grep "inet addr:"
else
ifconfig | grep "inet addr:" | awk '{print $2}'
fi
fi
if [ $ifflag -eq 1 ]; then
if [ $verbose -eq 1 ]; then
ifconfig $interface | grep "inet addr:"
else
ifconfig $interface | grep "inet addr:" | awk '{print $2}'
fi
fi
bash编程:循环控制 return,break
continue: 提前进入下一轮循环
用于条件语句中,仅在某些个特殊场景提前进入;
break [n]:跳出当前循环
用于条件语句中
例如:查看用户登录
#!/bin/bash
#
read -p "Plz enter a username: " userName
while true; do
if who | grep "\<$userName\>" &> /dev/null; then
break
fi
echo "not here."
sleep 5
done
echo "$userName is logged on."
#!/bin/bash
#
read -p "Plz enter a username: " userName
until who | grep "\<$userName\>" &> /dev/null; do
sleep 5
echo "not here"
done
echo "here"