Bash脚本编程总结
2016-03-12 00:00
543 查看
Bash脚本编程总结
在Linux中的脚本编程,通常指的是shell脚本,即bash脚本编程;bash作为一个命令的解释器,而bash脚本是将命令集合于一个或多个文本文件中,按照预设的顺序依次执行,来完成特定的,复杂的系统管理任务;本文将介绍bash编程的基本语法。基本格式:
#!/bin/bash
第一行必须顶格写,用shebang定义指定的解释器来解释该脚本;其它的以#开头的行均为注释,会被解释器忽略;可用作注释该脚本版本和用途,方便管理。
示例:
#!/bin/bash# Author:Chencer
# Date:2015/10/08 14:30
# Email:ms_achencer@yahoo.com
# Version:1.0.0
# Description:A test script.
执行方式:bash脚本属于过程式编程语言;
顺序执行
选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支;
循环执行:将同一段代码反复执行多次;因此,循环必须有退出条件;否则,则陷入死循环;
bash执行选项:
-n:语法测试; -x:模拟单步执行;
bash脚本变量:
什么是变量:有名称的内存空间;变量的命名:
赋值:Bash属于弱类型语言,任何变量无需事先声明,可直接使用,值默认都是字符型;
name=chencer name=”hello chencer”
撤销变量:
# unset varname
命名要求:
1、不能使用程序中的关键字(保留字);
2、只能使用数字、字母和下划线,且不能以数字开头
3、要见名知义
变量的类型:
数值型:
精确数值:整数
近似数值:浮点型
单精度浮点,双精度浮点
字符型:char,string
布尔型:true,false
变量声明命令:
# declare -a:array,数组; -i:intger,整数; -x:env, -r:readonly,只读; # export
类型转换:显式转换,隐式转换;
变量引用:
$name ${name}
变量的类别:
本地变量:只对当前shell进程有效的变量,对其它shell进程无效,包当前shell进程的子进程
> VAR_NAME=VALUE :变量赋值,向变量的存储空间保存数据; > ${VAR_NAME} :变量引用; “”:弱引用,变量会被替换为变量值; ‘’:强引用,字符会被直接输出;
环境变量:向变量的存储空间保存数据;
> export VAR_NAME=VALUE :定义变量; > export VAR_NAME :导出变量; > unset VAR_NAME :撤销变量; > readonly VAR_NAME :只读变量;
局部变量:对shell脚本中某代码片断有效,通常用于函数本地;
> local VAR_NAME=VALUE
位置变量:用来接受变量指定位置的参数;
> $1, ..., $n, ${10}
特殊变量:
$?: $#:传递给脚本参数的个数 $*: $@:引用传递给脚本的所有参数
bash的配置文件:
profile类:为交互式登录的用户提供配置
/etc/profile,/etc/profile.d/*.sh :全局; ~/.bash_profile :用户; 功用:设定环境变量,运行命令或脚本;
bashrc类:为非交互式的用户提供配置
/etc/bashrc :全局; ~/.bashrc :用户; 功用:设定本地变量,定义命令别名;
Bash算术运算与逻辑运算:
算术运算:如何定义整型变量:
# let VAR_NAME=INTEGER_VALUE 例如: # let a=3 # declare -i VAR_NAME=INTEGER_VALUE 例如: # declare -i a=3
注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算;bash会执行变量类型的隐式类型转换;
实现算术运算的方式:
# let VAR_NAME=ARITHMATIC_EXPRESSION # VAR_NAME=$[ARITHMATIC_EXRESSION] # VAR_NAME=$((EXPRESSION)) # VAR_NAME=$(expr $num1 + $num2)
算术运算符:
+:加法运算 -:减法运算 *:乘法运算; /:除法运算 %:取模运算,取余数; 例如:5%2=1, **:乘幂运算; 例如:2**2=4
逻辑运算:
布尔运算:
真,假
与、或、非、异或:
与运算: 真 && 真 = 真 真 && 假 = 假 假 && 真 = 假 假 && 假 = 假 或运算: 真 || 真 = 真 真 || 假 = 真 假 || 真 = 真 假 || 假 = 假 非运算: !真 = 假 !假 = 真
bash条件测试:
命令执行成功与否即为条件测试:test EXPR [ EXPR ] [[ EXPR ]]
命令都有其状态返回值:
成功:0,真 失败:1-255,假
测试类型:根据比较时的操作数的类型
整型测试:整数比较
字符测试:字符串比较
文件测试:判断文件的存在性及属性等
注意:比较运算通常只在同一种类型间进行
整型测试:
例如:[ $num1 -gt $num2 ] -gt:测试大于 -lt:测试小于 -ge:大于等于 -le:小于等于 -eq:测试相等 -ne:测试不等
字符串测试:
双目: 例如:[[ "$str1" > "$str2" ]] >:测试大于 <:测试小于 >=:大于等于 <=:小于等于 ==:测试相等 !=:测试不等 单目: -n String:是否不空,不空则为真,空则为假 -z String:是否为空,空则为真,不空则假
文件测试:判断文件属性等信息;
-a/-e FILE:判断存在; -f FILE:普通文件; -d FILE:目录文件; -L/-h FILE:符号链接文件; -b FILE:块设备文件; -c FILE:字符设备文件; -S FILE:套接字文件; -p FILE:命名管道; -s FILE:非空文件; -r FILE:可读; -w FILE:可写; -x FILE:可执行 file1 -nt file2:file1的mtime新于file2则为真,否则为假; file1 -ot file2:file1的mtime旧于file2则为真,否则为假;
组合条件测试:在多个条件间实现逻辑运算
与:[ condition1 -a condition2 ] condition1 && condition2 或:[ condition1 -o condition2 ] condition1 || condition2 非:[ -not condition ] ! condition
Bash脚本语句:
for循环语句:有两种格式;格式一:
For 变量名 in 列表do 循环体 done for:遍历有限的元素列表 列表:中间包括一个或多个元素; 循环体:依赖于调用变量来实现其变化
格式二:
for ((初始条件;测试条件;修改表达式));do 循环体 done
示例:使用两种for循环格式求100以内所有正整数之和;
#!/bin/bash#
declare -i sum=0
for i in {1..100};do
let sum+=$i
done
echo “sum = $sum”
#!/bin/bash#
declare -i sum=0
for ((i=1;$i <= 100;i++));do
let sum+=$i
done
echo “sum = $sum”
补充:语句中可嵌套语句;
示例:使用for语句中嵌套if语句,求100以内所有偶数之和,和基数之和;
#!/bin/bash#
declare -i evensum=0
declare -i oddsum=0
for i in `seq 1 100`;do
if [ $[$i%2] -eq 0 ];then
let evensum+=$i
else
let oddsum+=$i
fi
done
echo "evensum = $evensum"
echo "oddsum = $oddsum"
If判断语句:
语法格式:
If 条件1;then 分支1 elif 条件2;then 分支2 …… else 分支n fi
补充:bash编程之交互编程
> read -p "prompt":指定一个变量接收参数; -t timeout:超时时间;
示例:
传递一个用户给脚本;
如果用户不存在,则显示此用户不存在;
如果用户的id为0,则显示说这是管理员;
如果用户的id大于等于500,则显示说这是普通用户;
如果用户的id大于0小于500,则显示说这是系统用户;
#!/bin/bash#
read -p "Please input an username:" username
userid=`id -u $username`
if ! `id $username &>/dev/null`;then
echo "User $username not exsits."
exit 1
elif [ $userid -eq 0 ];then
echo "User $username is admin."
elif [ $userid -ge 500 ];then
echo "User $username is common user."
else
echo "User $username is system user."
fi
循环测试语句while和until:
while循环:首先进行条件测试,如果结果为真,则进入循环,执行循环体;结果为假,则退出循环;通常用于循环次数未知,或不便用for直接生成较大的列表时;
until循环:与while相反,条件测试结果为假则进入循环,为真则退出循环。
语句格式:
while 条件测试; do 循环体 done until 条件测试; do 循环体 done
示例:
使用while和until语句,求100以内所有偶数之和;
#!/bin/bash#
declare -i num=1
declare -i evensum=0
while [ $num -le 100 ]; do
if [ $[$num%2] -eq 0 ]; then
let evensum+=$num
fi
let num++
done
echo "evensum=$evensum."
#!/bin/bash#
declare -i num=1
declare -i oddsum=0
until [ $num -gt 100 ]; do
if [ $[$num%2] -eq 0 ]; then
let oddsum+=$num
fi
let num++
done
echo "oddsum=$oddsum."
提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;
#!/bin/bash#
read -p "Please enter a username:" username
while [ "$username" != 'q' -a "$userName" != 'quit' ]; do
if id $username &> /dev/null; then
grep "^$username\>" /etc/passwd | cut -d: -f3,7
else
echo "No such user."
fi
read -p "Pease enter a username again: " username
done
while循环特殊用法:遍历文件;
语句格式:
while read 变量名; do 循环体 done < /path/to/somefile
变量名:每循环一次,记忆了文件中一行文本
示例:
显示其ID号为偶数的用户的用户名、ID号和SHELL
#!/bin/bash#
while read line; do
userid=`echo $line | cut -d: -f3`
if [ $[$userid%2] -eq 0 ];then
echo $line | cut -d: -f1,3,7
fi
done < /etc/passwd
显示ID号为偶数,且ID号同GID的用户的用户名、ID和SHELL;
#!/bin/bash#
while read line; do
userid=`echo $line | cut -d: -f3`
groupid=`echo $line | cut -d: -f4`
if [ $[$userid%2] -eq 0 -a $userid -eq $groupid ]; then
echo $line | cut -d: -f1,3,7
fi
done < /etc/passwd
case语句:有多个测试条件时,case语句会使得语法结构更明晰;
语句格式:
case 变量引用 in PATTERN1) 分支1 ;; PATTERN2) 分支2 ;; ... *) 分支n ;; esac
PATTERN:类同于文件名通配机制,但支持使用|表示或者;
a|b:a或者b *:匹配任意长度的任意字符 ?:匹配任意单个字符 []:指定范围内的任意单个字符
示例:用户键入字符后判断其所属的类别;
#!/bin/bash#
read -p "Please enter a char:" char
case $char in
[[:digit:]])
echo "A digit."
;;
[[:alpha:]])
echo "A char."
;;
*)
echo "A special word."
;;
esac
函数:
可调用:使用函数名,函数名出现的地方,会被自动替换为函数体中的内容;
语句格式:
function F_NAME{ 函数体 } F_NAME() { 函数体 }
函数的返回值:
函数的执行结果返回值:代码的输出
函数中的打印语句:echo,print
函数中调用的系统命令执行后返回的结果
执行状态返回值:
最后一次执行的命令状态结果
自定义函数执行状态的返回值:return [0-255]
注意:return与exit;
return:遇到return语句,返回但不退出;
exit:遇到exit语句,返回且退出;
示例:
写一个脚本,完成如下任务,其使用形式如下所示:
script.sh {start|stop|restart|status}
其中:
如果参数为空,则显示帮助信息,并退出脚本;
如果参数为start,则创建空文件/var/lock/subsys/script,并显示“Starting script successfully.”
如果参数为stop,则删除文件/var/lock/subsys/script,并显示“Stop script successfully.”
如果参数为restart,则删除文件/var/locksubsys/script并重新创建,而后显示“Restarting script successfully.”
如果参数为status,那么:如果文件/var/lock/subsys/script存在,则显示“Script is running...”,否则,则显示“Script is stopped.”
说明:script.sh是脚本文件名,在创建时,其名称可以自己随意定义,但如果其名称发生变量,上/var/lock/sussys/下的文件名也要随之而变;
#!/bin/bash#
srv=`basename $0`
lockfile=/var/lock/subsys/"$srv"
[ `id -u` -ne 0 ] && echo "Oly root." && exit 1
[ $# -ne 1 ] && echo "Please input an argument,{start|stop|restart|status}." && exit 2
start () {
if ! [ -e $lockfile ];then
touch $lockfile
echo "Starting $srv successfully."
return 0
else
echo "$srv is running,don't need start."
return 4
fi
}
stop () {
if [ -e $lockfile ];then
rm -f $lockfile
echo "Stop $srv successfully."
return 0
else
echo "$srv is stopped,don't need stop."
return 5
fi
}
status () {
if [ -e $lockfile ];then
echo "$srv is running."
return 0
else
echo "$srv is stopped."
return 0
fi
}
usage () {
echo "Argument erro,Plase input {start|stop|restart|status}."
return 0
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
usage
exit 3
;;
esac
信号捕捉:
trap命令用于在shell程序中捕捉到信号,之后可以执行一段程序来处理这一信号:
trap 'COMMAND' SIGINT(表示关闭进程)
示例:
写一个脚本,能够ping探测指定网络内的所有主机是否在线,当没有执行完时可接收ctrl+c命令退出。
#!/bin/bash#
quit () {
echo "Quit..."
}
trap 'quit;exit 2' SIGINT
cnetping () {
for c in {1..254};
do
if `ping -c 1 -W 1 $1.$c &> /dev/null`;then
echo "$1.$c is up."
else
echo "$1.$c is down."
fi
done
}
bnetping () {
for b in {0..255};do
cnetping $1.$b
done
}
anetping () {
for a in {0..255};do
bnetping $1.$a
done
}
nettype=`echo $1 | cut -d"." -f1`
netmask=$2
if [ $nettype -ge 1 -a $nettype -le 126 -a $netmask -eq 8 ];then
anetping $nettype
elif [ $nettype -ge 1 -a $nettype -le 191 -a $netmask -eq 16 ];then
bnetping $(echo $1 | cut -d'.' -f1,2)
elif [ $nettype -ge 1 -a $nettype -le 223 -a $netmask -eq 24 ];then
cnetping $(echo $1 | cut -d'.' -f1-3)
else
echo "Wrong net."
exit 1
fi
循环控制:
continue:提前进入下一轮循环;用于条件语句中; break:提前跳出当前循环;用于条件语句中;
示例:
求出1-100之内,3的正整数之和;
#!/bin/bash#
sum=0
for i in {1..100};do
if [ $[$i%3] -ne 0 ];then
continue
fi
let sum+=$i
done
echo $sum
写一个脚本,判断给定的用户是否登录了当前系统:
如果登录了,则脚本终止;
每5秒种,查看一次用户是否登录;
#!/bin/bash#
while true; do
who | grep "script" &> /dev/null
if [ $? -eq 0 ];then
break
fi
sleep 5
done
echo "script is logged."
shift:如果没有数字,只有shift就是跳过一个参数获取下一个参数,如果加上数字,比如shift 2,跳过两个参数获取下一个参数。
示例:
写一个脚本,使用形式如下所示
showifinfo.sh [-i INTERFACE|-a] [-v]
要求:
-i或-a不可同时使用,-i用于指定特定网卡接口,-a用于指定所有接口;
显示接口的ip地址;
使用-v,则表示显示详细信息;
显示接口的ip地址、子网掩码、广播地址;
默认表示仅使用-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中只支持一堆数组,支持稀疏格式,参数个数没有限制;
表述方法:数组名+索引
索引的表示方法:
a[index] :数字索引,例如:a[0]、a[1]; a[hello]、a[hi] :关联数字;
声明:
declare -a ARRAR_NAME :表示普通数组;默认,可不用声明; declare -A ARRAR_NAME :表示数值;必须声明,bash需4.0以上版本支持,可通过bash -version查看当前版本;
数组元素赋值:
a[0]=$RANDOM :一次对一个元素赋值; a=(red blue yellow green) :一次对全部元素赋值; a=([0]=green [3]=red [2]=blue [6]=yellow) :按索引进行赋值; logs=(/var/log/*.log) :命令替换; read -a ARRAY :用户输入;
数组的访问:
用索引访问:
ARRAY[index]
数组的长度:
${#ARRAY[*]} ${#ARRAY[@]}
从数组中挑选某元素:
${ARRAY[@]:offset:number} 切片: offset: 偏移的元素个数 number: 取出的元素的个数 ${ARRAY[@]:offset}:取出偏移量后的所有元素 ${ARRAY[@]}: 取出所有元素
数组复制:
要使用${ARRAY[@]} $@: 每个参数是一个独立的串 $*: 所有参数是一个串
从数组中删除元素:
unset ARRAY[index]
示例:
复制一个数组中下标为偶数的元素至一个新数组中
#!/bin/bashdeclare -a mylogs
logs=(/var/log/*.log)
echo ${logs[@]}
for i in `seq 0 ${#logs[@]}`; do
if [ $[$i%2] -eq 0 ];then
index=${#mylogs[@]}
mylogs[$index]=${logs[$i]}
fi
done
echo ${mylogs[@]}
生成10个随机数,升序排序
#!/bin/bash#
for((i=0;i<10;i++));do
rnd[$i]=$RANDOM
done
echo -e "total=${#rnd[@]}\n${rnd[@]}\nBegin to sort"
for((i=9;i>=1;i--));do
for((j=0;j<i;j++));do
if [ ${rnd[$j]} -gt ${rnd[$[$j+1]]} ] ;then
swapValue=${rnd[$j]}
rnd[$j]=${rnd[$[$j+1]]}
rnd[$[$j+1]]=$swapValue
fi
done
done
echo ${rnd[@]}
打印九九乘法表
#!/bin/bash#
for((i=1;i<=9;i++));do
strLine=""
for((j=1;i<=9;j++));do
strLine=$strLine"$i*$j="$[$i*$j]"\t"
[ $i -eq $j ] && echo -e $strLine && break
done
done
字符串操作:
${string:offset:length} :字符串切片; ${string: -length} :取尾部的指定个数的字符;
示例:
[root@script ~]# string='hello chencer' [root@script ~]# echo $string hello chencer [root@script ~]# echo ${string:2:4} llo [root@script ~]# echo ${string: -2} er
取子串:基于模式;
${variable#*word}:在variable中存储字串上,自左而右,查找第一次出现word,删除字符开始至此word处的所有内容; ${variable##*word}:在variable中存储字串上,自左而右,查找最后一次出现word,删除字符开始至此word处的所有内容; ${variable%word*}: 在variable中存储字串上,自右而左,查找第一次出现word,删除此word处至字串尾部的所有内容; ${variable%%world*}:在variable中存储字串上,自右而左,查找最后一次出现word,删除此word处至字串尾部的所有内容;
示例:
[root@script ~]# file='/var/log/messages' [root@script ~]# echo $file /var/log/messages [root@script ~]# echo ${file#*/} var/log/messages [root@script ~]# echo ${file##*/} messages [root@script ~]# echo ${file%/*} /var/log [root@script ~]# echo ${file%%/*} 返回结果为空 [root@script ~]# phonenumber='010-110-8' [root@script ~]# echo $phonenumber 010-110-8 [root@script ~]# echo ${phonenumber%%-*} 010 [root@script ~]# echo ${phonenumber##*-} 8 [root@script ~]# url="http://www.chencer.org:80" [root@script ~]# echo $url http://www.chencer.org:80 [root@script ~]# echo ${url##*:} 80 :取端口; [root@script ~]## echo ${url%%:*} http :取协议;
查找替换:
${variable/pattern/substi}:替换第一次出现; ${variable//pattern/substi}:替换所有的出现; ${variable/#pattern/substi}:替换行首被pattern匹配到的内容; ${variable/%pattern/substi}:替换行尾被pattern匹配到的内容;
pattern可以使用globbing中的元字符:* ?
示例:
[root@script ~]# userinfo=`tail -n 1 /etc/passwd` [root@script ~]# echo $userinfo script:x:1000:1000::/home/script:/bin/bash [root@script ~]# echo ${userinfo/script/chencer} chencer:x:1000:1000::/home/script:/bin/bash [root@script ~]# echo ${userinfo//script/chencer} chencer:x:1000:1000::/home/chencer:/bin/bash [root@script ~]# echo ${userinfo/#script/chencer} chencer:x:1000:1000::/home/script:/bin/bash [root@script ~]# echo ${userinfo/%bash/chencer} script:x:1000:1000::/home/script:/bin/chencer
查找删除:
${variable/pattern}:删除第一次出现; ${variable//pattern}:删除所有的出现; ${variable/#pattern}:删除行首匹配到的内容; ${variable/%pattern}:删除行尾匹配到的内容;
示例:
[root@script ~]# echo ${userinfo/script} :x:1000:1000::/home/script:/bin/bash [root@script ~]# echo ${userinfo//script} :x:1000:1000::/home/:/bin/bash [root@script ~]# echo ${userinfo/#script} :x:1000:1000::/home/script:/bin/bash [root@script ~]# echo ${userinfo/%bash} script:x:1000:1000::/home/script:/bin/
大小写转换:
${variable^^} :小-->大; ${variable,,} :大-->小;
示例:
[root@script ~]# name=chencer [root@script ~]# echo $name chencer [root@script ~]# echo ${name^^} CHENCER [root@script ~]# name=CHENCER [root@script ~]# echo $name CHENCER [root@script ~]# echo ${name,,} chencer
变量赋值操作:
${variable:-string} :variable为空或未设定,那么返回string,否则,返回variable变量的值; ${variable:=string} :variable为空或未设定,则返回string,且将string赋值给变量variable,否则,返回variable的值;
为脚本使用配置文件,并确保某变量有可用值的方式
variable=${variable:-default vaule}
相关文章推荐
- 关于shell局部变量和全局变量
- shell订时检测sshd的端口
- linux下shell编程梳理
- 查找两个文件包含的字符串或不包含的字符串shell脚本
- 在 Visual Studio Code 中使用 PoweShell - CodeShell
- 系统加固shell脚本
- shell中正则的用法(grep sed awk)
- shell数组用法
- adb shell commands
- shellinabox安装
- shell 逻辑表达式汇总(if,大小比较)
- shell 脚本
- Unix-Shell
- shell 自动重启nginx php shell脚本
- Linux常用shell命令大全
- spark-shell 执行脚本并传入参数
- LinuxShell学习笔记
- telnet不能用!提示:-bash: telnet: command not found
- bash: mysql: command not found
- 使用xshell时中文横着显示的解决办法