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"
转载于:https://blog.51cto.com/aolens/1535021