Linux系统shell脚本
shell脚本编程:
基础编程:
程序:指令+数据
程序编程风格:
过程式:以指令为中心,数据服务于指令
对象式:以数据为中心,指令服务于数据
程序的执行方式:
计算机:运行二进制指令;
编程语言:
低级:汇编
高级:编译、解释
编译:高级语言—–>编译器——–>目标代码 (Java、c#)
解释:高级语言—–>解释器——–>机器代码 (shell、Perl、Python)
编程基本概念:
编程逻辑处理方式:
顺序执行
循环执行
选择执行
shell编程:过程式、解释执行
编程语言的基本结构:
数据存储:变量数组
表达式:a + b
语句:if
shell脚本基础:
shell脚本是包含一些命令或声明,并符合一定格式的文本文件;
格式要求:首行shabang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
shell 脚本的用途:
自动化常用命令
执行系统管理和故障排除
创建简单的应用程序
处理文本或文件
shell脚本的创建:
第一步:使用文本编辑器来创建文本文件;
第一行必须包括shell声明:
#!/bin/bash
添加注释行:
注释行以#开头
添加运行命令:
…………………….
第二步:运行脚本文件:
给予执行权限,在命令行上指定脚本的绝对路径或相对路径
直接运行解释器,将脚本作为解释器程序的参数
脚本调试:
检测脚本中的语法错误:
bash -n /path/to/somescript
调试执行脚本:
bash -x /path/to/somescript
shell变量应用:
本地变量:生效范围为当前shell进程,对当前shell的子shell及其它shell进程均无效;
环境变量:生效范围为当前shell进程及其子shell进程;
局部变量:生效范围为当前shell进程中某代码片段(通常指函数);
变量命名法则:
1、不能使用程序中的保留字,例如:if、for等;
2、只能使用数字、字母、及下划线,且不能以数字开头;
3、见名知意;
4、统一命名规范;
本地变量基本格式:
变量名=变量值
例:
[root@centos7 ~]# day=sunday
[root@centos7 ~]# echo $day
sunday
[root@centos7 ~]#
注意:当变量名称容易和紧跟其后的其他字符相混淆时,需要添加 “{ }”将其包围起来进行区分,否则将无法确定正确的变量名。
例:
[root@centos7 ~]# day=sunday
[root@centos7 ~]# echo ${day}hayyp
sundayhayyp
[root@centos7 ~]#
为变量赋值的常用方法:
在等号“=”后面直接指定变量内容是为变量赋值的最基本的方法,除此之外我们还可以使用 “双引号”、“单引号”、“反撇号”、“read命令”。
双引号:
使用双引号时,允许在双引号的范围内使用$符号来引用其他变量的值;
“ ”:弱引用,双引号中可以引用其他变量的值;
用法:name=”root” 或 name=”$USER”
例:
[root@centos7 ~]# echo $day
sunday
[root@centos7 ~]# today="today is $day"
[root@centos7 ~]# echo $today
today is sunday
[root@centos7 ~]#
单引号:
使用单引号时,将不允许在单引号的范围内引用其他变量的值;在单引号中“$”符或其他任何符号将作为普通字符看待。
‘ ’:强引用,单引号中变量引用不会被替换为变量值,都当做普通字符看待;
例:
[root@centos7 ~]# echo $day
sunday
[root@centos7 ~]# today='today is $day'
[root@centos7 ~]# echo $today
today is $day
[root@centos7 ~]#
反撇号:
使用反撇号时,允许将执行特定命令的输出结果赋值给变量;
用法:name=`command` 或 name=$(command)
例:
server_ip=$(ifconfig | sed -n "2p" | grep -E -o '(([0-9]|[1-9][0-9]|1[0-9]{2}| 2[0-4][0-9]|25[0- 5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' | head -1 )
或
login_time=`date +%F-%T`
host_name=`hostname`
read命令:
我们可以使用bash的内置命令read来给变量赋值,read命令可以从终端键盘读取输入,实现简单的交互过程。read将从标准输入读入一行内容,并以空格为分隔符,将读入的各字段分别赋值给列表中指定的变量,多余的内容赋值给最后一个变量。
为了使交互操作更加友好,提高易用性,我们可以结合“-p”选项来设置提示信息,用于告知用户应该输入的内容。
例:
[root@centos7 profile.d]# read -p "please input a number :" number
please input a number :123
[root@centos7 profile.d]# echo $number
123
[root@centos7 profile.d]#
显示已定义的所有变量(环境变量和本地变量局部变量):set命令
删除本地变量:unset 变量名 (可以指定一个或多个变量名做参数)
环境变量:
为了使用户定义的本地变量在所有的子shell环境中能够继续使用,减少重复设置工作,可以使用export命令将指定的变量设置为“全局变量”。export命令可以同时使用多个变量名作为参数,变量名之间以空格分隔。
用法:1、 export 变量名=变量值
或
2、 变量名=变量值
export 变量名
或
3、declare -x 变量名=变量值
declare -r 变量名=变量值(只读变量,不能修改和删除)
例:
现在在子shell中也可以使用父shell定义的环境变量
[root@centos7 Desktop]# export day=sunday
[root@centos7 Desktop]# echo $day
sunday
[root@centos7 Desktop]# bash
[root@centos7 Desktop]# echo $day
sunday
[root@centos7 Desktop]#
显示所有环境变量:
export
env
printenv
declare -x
删除环境变量:
unset 变量名
只读变量:
只读变量:只能声明,不能修改和删除;
readonly 变量名=变量值
declare -r 变量名=变量值
例:
[root@centos7 Desktop]# readonly today="today is sunday"
[root@centos7 Desktop]# declare -r year=2016
位置变量:
位置变量:用于让shell脚本在执行过程中可以调用命令行中给出的位置参数;
位置变量采用“$n”的格式,其中“n”是参数的位置序号从( 1-9 )
例:$1 、$2、$3 、$4 、$5 、$6 、$7 、$8 、$9 、
特殊变量:
特殊变量是由bash程序预先定义好的一些特殊变量,用户只能使用这些特殊变量,不能创建新的特殊变量或直接为特殊变量赋值。
$0:表示命令本身;
$#:表示传递给脚本的参数个数;
$*:表示所有位置参数的内容,全部参数合为一个字符串;
$@:传递给脚本的所有参数,每个参数为独立的字符串;
$?:表示命令执行后返回的状态,命令退出状态为0表示命令执行正确,任何非0值得表示命令执行错误,(错误值1-255之间的任意数值)
$$:表示当前进程的进程号;
$!:表示后台运行的最后一个进程的进程号;
注:$#、$@只在被双引号包起来的时候才会有差异;
数值变量运算:
运算符:
“+”:加法运算;
“–”:减法运算;
“*”:乘法运算,直接用expr运算时需要转义“\*”;
“/”:除法运算;
“**”:乘方;
“%”:取模运算,又称为取余运算,即计算数值相除后的余数;
算数运算格式:
let var=算术运算表达式
var=$[算术运算表达式]
var=$((算术运算表达式))
var=$(expr $ARG1 $OP $ARG2 )
declare -i var=算术运算表达式
echo ‘算术运算表达式’ |bc
例:
[root@centos7 ~]# let sum=$num1+$num2
[root@centos7 ~]# sum=$[$num1+$num2]
[root@centos7 ~]# sum=$(($num1+$num2))
[root@centos7 ~]# sum=$(expr $num1 \* $num2)
[root@centos7 ~]# sum=`expr $num1 \* $num2`
[root@centos7 ~]# declare -i var=$num1+$num2
bash有内建的随机数生成器:$RANDOM (1-32767)
echo $[$RANDOM%50] :0-49之间的随机数;
赋值:
增强型赋值:
+= -= *= /= %=
let varOPERvalue
let count +=3 意思是:let count=count+3
自增,自减
let var+=1意思是:let var=var+1
let var++ 意思是:let var=var
let ++var 意思是:let var=var+
let var-=1 意思是:let var=var-1
let var– 意思是:let var=var
逻辑运算:
运算数:
真(true、yes、on、1)
假(false、no、off、0)
与:
1 && 1 = 1
1 && 0 = 0
0 && 1 = 0
0 && 0 = 0
或:
1 || 1 = 1
1 || 0 = 1
0 || 1 = 1
0 || 0 = 0
非:
! 1 = 0
! 0 = 1
短路法则:
~]# command1 && command2
command1为“假”,则command2不会执行操作;
反之,command1为“真”,则command2必须执行;
~]# command1 || command2
command1为“真”,则command2不会再执行;
反之,command1为“假”,则command2必须执行;
聚集命令:
有两种聚集命令的方法:
1)复合式:date;who | wc -l
2)子shell:(date; who | wc -l)在子shell中运行的;
退出状态:
进程使用退出状态来报告成功或失败;
$?:变量中保存了最近一次命令执行状态值;
0:代表成功;
1-255:代表失败;
条件测试:
判断某需求是否满足,需要由测试机制来实现;
专用的测试表达式需要由测试命令辅助完成测试过程;
使用test测试命令时,可以有以下两种形式;
test 条件表达式
或
[ 条件表达式 ]
[[ 条件表达式 ]]
注意:条件表达式两端必须有空白字符,否则为语法错误;
bash的测试类型
文件测试:
数值测试:
字符串测试:
逻辑测试:
文件测试:
-d:测试是否为目录;
-e:测试目录或文件是否存在;
-c:测试是否为字符设备文件;
-f:测试是否为文件;
-r:测试当前用户是否有权限读取;
-w:测试当前用户是否有权限写入;
-x:测试当前用户是否可执行该文件;
-L:测试是否为符号链接(link)文件;
-p:测试是否为管道文件;
-S:测试是否为套接字文件;
-s:测试文件是否存在且非空;
-g:测试是否拥有sgid权限;
-u:测试是否拥有suid权限;
-k:测试是否拥有sticky权限;
-N:测试文件自上一次被读取之后是否被修改过;
-O:测试当前有效用户是否为文件属主;
-G:测试当前有效用户是否为文件属组;
file1 -ef file2 :测试file1与file2是否指向同一个设备上的相同inode
file1 -nt file2 :测试file1是否新于file2;
file1 -ot file2 :测试file1是否旧于file2;
例:
[root@centos7 ~]# [ -f /etc/passwd ] && echo yes
yes
[root@centos7 ~]#
数值测试:
数值比较:
-eq:第1个数等于第二个数;
-ne:第1个数不等于第二个数;
-gt:第1个数大于第二个数;
-lt :第1个数小于第二个数;
-ge:第1个数大于或等于第二个数;
-le:第1个数小于或等于第二个数;
例:
[root@centos7 ~]# sum=`cat /etc/passwd | wc -l`
[root@centos7 ~]# echo $sum
43
[root@centos7 ~]# [ $sum -ge 10 ] && echo "yes"
yes
[root@centos7 ~]#
字符串测试:
==:是否等于;
>:是否大于;
<:是否小于;
!=:是否不等于;
=~:左侧字符串是否能够被右侧的pattern所匹配;
-z “string”:判断指定的字符串是否为空,空则真,不空则假;
-n “sting” :判断指定的字符串是否不空,空则假,不空则真;
-a : 并且的意思;
注意:
(1)字符串要加双引号;
(2)要使用[[ ]]进行测试;
例:
测试$LANG变量是否等于en.US:
[root@centos7 ~]# [[ $LANG == en.US ]] || echo $LANG
en_US.UTF-8
[root@centos7 ~]#
测试abc字符串是否不等于def字符串:
[root@centos7 ~]# [[ "abc" != "def" ]] && echo "yes" || echo "no"
yes
[root@centos7 ~]#
测试文件是否为空:
[root@centos7 ~]# [[ -z `cat file1` ]] && echo "yes"
yes
[root@centos7 ~]#
测试文件是否为不空:
[root@centos7 ~]# [[ -n `cat file1` ]] && echo "yes"
yes
[root@centos7 ~]#
判断a是否小于b:
[root@centos7 ~]# [[ "a" < "b" ]] && echo "yes"
yes
[root@centos7 ~]#
判断aa是否能够被模式[ac]匹配:
[root@centos7 ~]# [[ “aa” =~ [ac] ]] && echo "yes" || echo "no"
yes
[root@centos7 ~]#
逻辑测试:
逻辑测试是指同时使用两个或(多个)条件表达式之间的关系;
&&逻辑与:表示前后两个表达式都成立时整个测试结果才为真;否则为假;
||逻辑或:表示前后两个条件至少有一个成立时整个测试结果即为真,否则结果为假;
!逻辑否:表示当指定的条件表达式不成立时,整个测试结果的命令为真;
例:
测试/etc/profile文件是否有执行权限:
root@centos7 ~]# [ ! -x /etc/profile ] && ls -l /etc/profile || echo "have x mode"
-rw-r–r–. 1 root root 1750 jun 7 2013 /etc/profile
[root@centos7 ~]#
shell脚本编程语言:
过程式编程语言:
顺序执行
选择执行
循环执行
if语句的结构:
单分支的if语句:
if 条件测试命令
then
命令序列1
fi
双分支if语句:
if 条件测试命令
then
命令序列1
else
命令序列2
fi
多分支的if语句:
if 条件测试命令1
then
命令序列1
elif 条件测试命令2
then
命令序列2
else
命令序列3
fi
case语句:
case语句适用于需要进行多重分支的应用情况;
case支持glob风格的通配符:
*:任意长度任意字符;
?:任意单个字符;
[ ]:指定范围内的任意单个字符;
a|b:a或b语句;
case 变量引用 in
pat1)
命令序列1
;;
pat2)
命令序列2
;;
pat3)
命令序列3
;;
*)
默认执行的命令序列
esac
注意:case行尾必须为单词“in”,每一模式必须以右括号“)”结束;
双分号“;;”表示命令序列的结束;
循环:
循环执行:
将某段代码重复运行多次;
循环次数事先已知;
循环次数事先位置;
循环时执行进入条件和退出条件;
循环语句:
for while until
for循环:
for 变量名 in 列表;do
循环体
done
依次将列表中的元素赋值给“变量名”;每次赋值后即执行依次循环体;直到列表中的元素耗尽,循环结束。
示例:
for循环 99乘法表
1 #!/bin/bash
2 #multiplicationtables
3
4 for i in {1..9}
5 do
6 for j in {1..9}
7 do
8 if [ $i -ge $j ];then
9 let sum=$j*$i
10 echo -n -e "$j*$i=$sum\t"
11 fi
12 done
13 echo
14 done
17 unset i
18 unset j
19 unset sum
或
1 #!/bin/bash
2 #zheng
3
4 for ((i=1;i<=9;i++));do
5
6 for ((j=1;j<=i;j++));do
7
8 echo -e -n "${j}x${i}=$[${i}*${j}]\t"
9 done
10 echo
11 done
12 unset i
13 unset j
while 循环:
while 条件测试判断 ;do
循环体
done
条件测试判断:循环控制条件;进入循环之前,先做一次判断,每一次循环之后会在次做判断;条件为“true”,则执行依次循环,直到条件测试状态为false终止循环;
因此:条件测试判断一般应该有循环控制变量;而此变量的值会在循环体不断的被修正;
进入条件:条件测试判断为true;
退出条件:条件测试判断为false;
示例:
测试192.168.3网段的在线主机状态,并统计在线主机和离线主机的个数;
1 #!/bin/bash
2 #grep ServerIp
3
4 ip=192.168.3.
5 net=1
6 i=0
7 j=0
8
9 while [ $net -lt 255 ];do
10 ping -c1 -w1 ${ip}$net &> /dev/null
11 if [ $? -eq 0 ];then
12 echo "此${ip}$net地址正常"
13 let i++
14 else
15 echo "此${ip}$net地址不正常"
16 let j++
17 fi
18
19 let net++
20 done
21echo "活跃用户:$i"
22 echo "不活跃用户:$j"
while循环打印国际围棋:
示例:
1 #!/bin/bash
2 #while打印国际围棋
3 i=1
4
5 while [ $i -le 8 ];do
6
7 j=1
8 while [ $j -le 8 ];do
9 let sum=$i+$j
10 let m=$sum%2
11 # [ $m -eq 0 ] &&echo -en "\033[41;1m \033[0m" || echo -en "\033[43;1m \033[0m"
12 if [ $m -eq 0 ];then
13 echo -en "\033[41;1m \033[0m"
14 else
15 echo -en "\033[43;1m \033[0m"
16 fi
17 let j++
18
19 done
20 echo
21 let i++
22
23 done
24 unset i
25 unset j
26 unset sum
until循环:
until 条件测试判断;do
循环体
done
进入条件:条件测试判断为false
退出条件:条件测试判断为true
示例:
猜数字游戏:
1 #!/bin/bash
2 #
3
4 echo "============================="
5 suiji=$(($RANDOM%10+1))
6 max=10
7 zuixiao=1
8
9 read -p "please input a number:" num
10
11 until [ $num -eq $suiji ];do
12
13 echo "你要猜$zuixiao 和 $max之间的数"
14
15 if [ $suiji -gt $num ];then
16
17 echo "你猜的太小了"
18 else
19 echo "你猜的太大了"
20 fi
21 read -p "please input a number:" num
22
23
24 done
25 echo "恭喜你猜对了"
26 unset suiji
27 unset max
28 unset zuixiao
continue命令:
continue即“继续”的意思,用于暂停本次循环,跳转至循环语句的顶部重新测试条件。
示例:
计算100以内所有偶数的和:
1 #!/bin/bash
2 #zheng
3
4
5 declare -i addsum=0
6 declare -i i=0
7
8 while [ $i -le 100 ];do
9 let i++
10 if [ $(($i%2)) -eq 1 ];then
11 continue
12
13 fi
14 let addsum+=$i
15
16 done
17
18
19 echo "oushu100 sum :$addsum "
20 unset addsum
21 unset i
break命令:
break即“终断”的意思,用于跳出当前所在的循环体,但是并不退出程序;
示例:
求100以内偶数的和:(使用while、break命令)
1 #!/bin/bash
2 #zheng
3
4 declare -i i=0
5 declare -i sum=0
6
7 while true ;do
8 ((i+=2))
9 if [ $i -gt 100 ];then
10
11 break
12 fi
13 ((sum+=$i))
14 done
15 echo " $sum"
16 unset i
17 unset sumg
示例:
求100以内偶数的和:(使用until、break命令)
1 #!/bin/bash
2 #zheng
3
4 declare -i i=0
5 declare -i sum=0
6
7 until false ;do
8 ((i+=2))
9 if [ $i -gt 100 ];then
10 break
11 fi
12 ((sum+=$i))
13 done
14 echo " $sum"
15 unset i
16 unset sum
示例:
求100以内jishu之和:(使用while、break命令:)
1 #!/bin/bash
2 #zheng
3
4 declare -i i=1
5 declare -i sum=0
6
7 while true ;do
8 if [ $i -gt 100 ];then
9 break
10 fi
11 ((sum+=$i))
12 ((i+=2))
13 done
14 echo " $sum"
15 unset i
16 unset sum
sleep 命令:
示例:
检测zheng用户是否登录系统:
1 #!/bin/bash
2 #zheng
3 #
4 while true;do
5
6 if who | grep "^zheng\>" &> /dev/null;then
7 break
8 fi
9 sleep 5
10 done
11
12 echo "zheng logged on " >> /var/log/messages
创建无线循环:
while true;do
循环体命令
done
until false;do
循环体命令
done
特殊用法:
while read line;do
循环体
done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将每行赋值给变量line;
示例:
扫描/etc/passwd/文件每一行,如发现描述字段为空,则填充用户名和单位电话为123456;
1 #!/bin/bash
2 #zheng
3
4 while read line;do
5 kong=$( echo $line| cut -d: -f5 )
6 if [ -z "$kong" ];then
7
8 user=$(echo $line | cut -d: -f1)
9 usermod -c "${user} 123456" $user &> /dev/null
10 echo "add user:$user"
11 fi
12
13 done </etc/passwd
14 unset kong
15 unset user
特殊用法:
for循环特殊用法:语法格式
for (( exp1; exp2; exp3 )); do COMMANDS; done
for循环的特殊格式:
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式));do
循环体命令
done
控制变量初始化:仅在运行到循环代码段时执行依次;
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后在做条件判断;
示例:
1 #!/bin/bash
2 #zheng
3
4 for (( i=0;i<=100;i++ ));do
5
6 let sum+=$i
7
8 done
9
10 echo "$sum"
11
12 unset sum
select 循环与菜单:
select 变量名 in list;do
循环体命令
done
select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入;
用户输入菜单中的某个数字,执行相应的命令
用户输入被保存在内置变量REPLY中;
select 与case
select 是个无限循环体,因此要记住使用break命令退出循环,或使用exit命令终止脚本。也可以按ctrl+c退出循环。
select 经常和case联合使用
与for循环类似。可以省略 in list。此时使用位置参量;
~ 示例:
1 #!/bin/bash
2 #zheng
3 #菜单
4 PS3="你好,想吃点什么,请点序号:"
5 select menu in 凉菜 热菜 土豆 梅菜扣肉 鸡蛋面;do
6 echo -n "你点的菜是: "
7
8 case $menu in
9
10 凉菜)
11 echo -e "\033[31m凉菜\033[0m"
12 ;;
13 热菜)
14 echo -e "\033[31m热菜\033[0m"
15 ;;
16 土豆)
17 echo -e "\033[31m土豆\033[0m"
18 ;;
19 梅菜扣肉)
20 echo -e "\033[31m梅菜扣肉\033[0m"
21 ;;
22 鸡蛋面)
23 echo -e "\033[31m鸡蛋面\033[0m"
24 ;;
25 *)
26 echo -e "\033[31m sorry,你想吃的本店没有!\033[0m"
27 ;;
28 esac
29 break
30
31
32 done
函数介绍:
函数function是由若干条shell命令组合成的语句块,实现代码重用和模块化编程。
函数function与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分。
函数和shell程序比较相似,区别在于:
shell程序在子shell中运行;
而shell函数在当前shell中运行。因此在当前shell中,函数可以对shell中的变量进行修改;
定义函数:
函数由两部分组成:函数名和函数体
语法一:
function f_name {
函数体…….
}
语法二:
f_name() {
函数体…….
}
函数的使用:
函数的定义和使用:
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:
函数只有被调用才会执行;
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:
函数被调用时创建,返回时终止;
函数返回值:
函数有两种返回值:
函数的执行结果返回值:
使用echo或printf命令进行输出
函数体中调用命令的输出结果
函数的退出状态码:
默认取决于函数中执行的最后一条命令的退出状态码
自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回;
return 1-255 有错误返回;
使用函数文件:
可以将经常使用的函数存入函数文件,然后将函数文件载入shell;
函数文件名可以任意选取,但最好与相关任务有某种联系。例如:function.main;
一旦函数文件载入shell,就可以在命令行或脚本中调用函数,可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
若要改动函数,首先使用unset命令从shell中删除函数,改动完毕后,在重新载入此文件;
载入函数:
函数文件已创建好,要将它载入shell
定义函数文件并载入shell的格式:
. function.main 或 source function.main要使用正确的路径
set命令检查函数是否已载入,set命令将在shell中显示所有的载入函数。
删除shell函数:
现在对函数做一些改动,首先删除函数,使其对shell不可用。使用unset命令完成此功能;
命令格式:
unset function_name
函数参数:
函数可以接受参数:
传递参数给函数:调用函数时,在函数名后以空白分隔给定参数列表即可;例如:testfunc arg1 arg2 arg3
在函数体中,可以使用$1,$2 ……. 调用这些参数;还可以使用$@,$#等特殊变量;
函数变量:
变量作用域:
环境变量:当前shell和子shell有效;
本地变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用域范围是当前shell脚本进程文件,包括脚本中函数。
局部变量:函数的生命周期;函数结束时变量被自动销毁;
注意:如果函数中有局部变量,如果其名同本地变量,使用局部变量;
在函数中定义局部变量的方法:
local name=value
函数递归实例:
函数递归:
函数直接或间接调用自身
需注意递归层数
示例:
函数递归阶乘运算
1 #!/bin/bash
2 #
3
4 fact () {
5 if [ $1 -eq 0 -o $1 -eq 1 ];then
6 echo 1
7 else
8 echo $(($1*`fact $[$1-1]`))
9 fi
10 }
11
12 fact $1
示例:
在脚本中定义及使用函数
1 #!/bin/bash
2 #fun1
3 #zheng
4 #加减乘除函数库!
5
6 jia() {
7 sum=$[$1+$2]
8 echo "$sum"
9 }
10
11 jian() {
12 sum=$[$1-$2]
13 echo "$sum"
14 }
15
16 cheng() {
17 sum=$[$1*$2]
18 echo "$sum"
19 }
20
21
22 chu() {
23 sum=$[$1/$2]
24 echo "$sum"
25 }
定义数组:
程序=指令+数据
变量:存储单个元素的内存空间;
数组:存储多个元素的连续的内存空间;
数组名:整个数组只有一个名字;
数组索引:标号从0开始;
数组名[索引]
${ARRAY_NAME[INDEX]}
注:bash-4及之后的版本,支持自定义索引格式,而不仅仅是0,1,2,3….数组格式,此类数组称之为关联数组;
声明数组:
declare -a ARRAY_NAME 声明索引数组
declare -A ARRAY_NAME 声明关联数组
数组中元素的赋值方式;
(1)一次只赋值一个元素;
ARRAY_NAME[INDEX]=value
例:
[root@centos7 ~]# dongwu[0]=pig
[root@centos7 ~]# dongwu[1]=dog
[root@centos7 ~]# echo $dongwu
pig
[root@centos7 ~]# echo ${dongwu[1]}
dog
[root@centos7 ~]# echo ${dongwu[0]}
pig
[root@centos7 ~]#
(2)一次赋值全部元素;
ARRAY_NAME=(“val1” “val2” “val3” “val4”…….)
例:
[root@centos7 ~]# weekday=("monday" "tuesday" "wednesday")
[root@centos7 ~]# echo ${weekday[0]}
monday
[root@centos7 ~]# echo ${weekday[1]}
(3)只赋值特定元素
ARRAY_NAME=([0]=”val1” [3]=”val2”…..)
注:bash支持稀疏格式的数组;
例:
[root@centos7 ~]# gongsi=([1]=baidu [3]=tengxun [4]=ali)
[root@centos7 ~]# echo ${gongsi[3]}
tengxun
[root@centos7 ~]#
(4)read -a ARRAY_NAME
例:
[root@centos7 ~]# read -a jiaotong
huoche qiche feiji kunchuan
[root@centos7 ~]# echo ${jiaotong[0]}
huoche
[root@centos7 ~]#
引用数组中的元素:${ARRAY_NAME[INDEX]}
注意:引用时,只给数组名,表示引用下标为0的元素;
数组的长度(数组中元素的个数)
${#ARRAY_NAME[*]} 表示查看数组中的元素个数;
${#ARRAY_NAME[@]}表示查看数组中的元素个数;
例:
[root@centos7 ~]# echo ${#jiaotong[*]}
4
[root@centos7 ~]#
${ARRAY_NAME[*]} 表示查看数组中的所有元素
${ARRAY_NAME[@]} 表示查看数组中的所有元素
例:
[root@centos7 ~]# echo ${jiaotong[*]}
huoche qiche feiji kunchuan
[root@centos7 ~]#
注意:如果写成${#ARRAY_NAME} 表示查看数组中第一元素的字符长度;
示例:生成10个随机数,并找出其中的最大值;
1 #!/bin/bash
2 #zheng
3 #
4
5 declare -a rand
6 declare -i max=0
7 for i in {0..9};do
8
9 rand[$i]=$RANDOM
10 echo ${rand[$i]}
11 if [ ${rand[$i]} -gt $max ];then
12 max=${rand[$i]}
13 fi
14 done
15 echo "max : $max"
16 unset rand
17 unset i
示例:定义一个数组,数组中的元素是/var/log目录下所有以*.log结尾的文件,统计其下标为偶数的文件中的行数之和;
1 #!/bin/bash
2 #zheng
3 #
4 declare -a files
5 files=(/var/log/*.log)
6 declare -i lines=0
7
8 for i in $(seq 0 $[${#files[@]}-1]);do
9
10 if [ $[$i%2] -eq 0 ];then
11 let lines+=$(wc -l ${files[$i]} | cut -d " " -f1)
12 fi
13 done
14 echo "lines :$lines"
15 unset files
16 unset lines
17 unset i
数组元素切片:
${ARRAY_NAME[@]:offset:number}
offset:要跳过的元素个数;
number:要取出的元素个数;省略number时,表还取偏移量之后的所有元素;
向非稀疏格式数组中追加元素:
ARRAY_NAME[${#array_name[]*}]=value
例:
[root@centos7 testdir]# echo ${weekday[*]}
monday tuesday wednesday
[root@centos7 testdir]# weekday[${#weekday[@]}]=sunday
[root@centos7 testdir]# echo ${weekday[*]}
monday tuesday wednesday sunday
[root@centos7 testdir]#
删除数组中的某个元素:
unset ARRAY[INDEX]
例:
[root@centos7 testdir]# unset weekday[3]
关联数组:
declare -A ARRAY_NAME (使用关联数组必须提前声明)
ARRAY_NAME=([index_name1]=value1 [index_name2]=value2)
bash的内置字符串处理工具:
字符串切片:
${var:offset:number}
offset:要跳过的元素个数;
number:要取出的元素个数;省略number时,表还取偏移量之后的所有元素;
取自符串的字串;
取字符串的最右侧的几个字符:${var: -length}
注意:冒号后必须有一个空白字符;
例:
[root@centos7 testdir]# echo ${weekday[*]}
monday tuesday wednesday sunday
[root@centos7 testdir]# echo ${weekday[@]:1:3}
tuesday wednesday sunday
[root@centos7 testdir]# echo ${weekday[@]: -1}
sunday
[root@centos7 testdir]#
基于模式取字串:
${var#*word}:其中Word是指定的分隔符;功能:自左而右,查找var变量所存储的字符串中,第一次出现的word分隔符,删除字符串开头至此分隔符之间的所有字符;
${var##*word}:其中Word是指定的分隔符;功能:自左而右,查找var变量所存储的字符串中,最后一次出现的word分隔符,删除字符串开头至此分隔符之间的所有字符;
示例:
[root@centos7 testdir]# path=/etc/init.d/functions
[root@centos7 testdir]# echo ${path#*/}
etc/init.d/functions
[root@centos7 testdir]# echo ${path##*/}
functions
[root@centos7 testdir]#
${var%word*}:其中Word是指定的分隔符;功能:自右而左,查找var变量所存储的字符串中,第一次出现的word分隔符,删除此字符串至尾部的所有字符;
${var%%word*}:其中Word是指定的分隔符;功能:自右而左,查找var变量所存储的字符串中,最后一次出现的word分隔符,删除此字符串至尾部的所有字符;
示例:
[root@centos7 testdir]# path=/etc/init.d/functions
[root@centos7 testdir]# echo ${path%/*}
/etc/init.d
[root@centos7 testdir]# echo ${path%%/*}
[root@centos7 testdir]#
查找替换:
${var/PATTERN/SUBSTI}:查找var所表示的字符串,第一次被PATTERN所匹配的字符串,将其替换为SUBSTI所表示的自符串;
${var//PATTERN/SUBSTI}:查找var所表示的字符串,所有被PATTERN所匹配的字符串,将其全部替换为SUBSTI所表示的自符串;
示例:
[root@centos7 testdir]# name="root:x:0:0::root:bashroot"
[root@centos7 testdir]# echo ${name/root/ROOT}
ROOT:x:0:0::root:bashroot
[root@centos7 testdir]# echo ${name//root/ROOT}
ROOT:x:0:0::ROOT:bashROOT
[root@centos7 testdir]#
${var/#PATTERN/SUBSTI} 查找var所表示的字符串中,行首被PATTERN所匹配到的字符串,将其替换为SUBSTI所表示的字符串;
${var/%PATTERN/SUBSTI} 查找var所表示的字符串中,行尾被PATTERN所匹配到的字符串,将其替换为SUBSTI所表示的字符串;
示例:
[root@centos7 testdir]# name="root:x:0:0::root:bashroot"
[root@centos7 testdir]# echo ${name/#root/ROOT}
ROOT:x:0:0::root:bashroot
[root@centos7 testdir]# echo ${name/%root/ROOT}
root:x:0:0::root:bashROOT
[root@centos7 testdir]#
注意:PATTERN中使用glob风格的通配符;
查找删除:
${var/PATTERN}:以PATTERN为模式查找var字符串中第一次的匹配,并删除;
${var//PATTERN}:以PATTERN为模式查找var字符串中所有的匹配,并删除;
${var/#PATTERN}:以PATTERN为模式查找var字符串中行首匹配,并删除;
${var/%PATTERN}:以PATTERN为模式查找var字符串中行尾匹配,并删除;
示例:
[root@centos7 testdir]# name="root:x:0:0::root:bashroot"
[root@centos7 testdir]# echo ${name/root}
:x:0:0::root:bashroot
[root@centos7 testdir]# echo ${name//root}
:x:0:0:::bash
[root@centos7 testdir]# echo ${name/#root}
:x:0:0::root:bashroot
[root@centos7 testdir]# echo ${name/%root}
root:x:0:0::root:bash
[root@centos7 testdir]#
字符大小写转换:
${var^^}:把var中的所有小写字符转换为大写;
${var,,}:把var中的所有大写字符转换为小写;
示例:
[root@centos7 testdir]# name="root:x:0:0::root:bashroot"
[root@centos7 testdir]# echo ${name^^}
ROOT:X:0:0::ROOT:BASHROOT
[root@centos7 testdir]# name1=${name^^}
[root@centos7 testdir]# echo $name1
ROOT:X:0:0::ROOT:BASHROOT
[root@centos7 testdir]# echo ${name,,}
root:x:0:0::root:bashroot
[root@centos7 testdir]#
变量赋值:
${var:-VALUE}:如果var变量为空,或未设置,那么返回value,否则返回var变量的值;
${var:=VALUE}:如果var变量为空,或未设置,那么返回value,并将value赋值给var变量;否则,则返回var变量的值;
${var:+VALUE}:如果var变量不空,则返回value
${var:?ERROR_INFO}:如果var为空,或未设置,那么返回ERROR_INFO为错误提示,否则,返回var值;
高级变量用法:
shell变量一般是无类型的,但是bash shell提供了declare和typeset两个命令用于指定变量的类型,两个命令是完全等价的
declare [options] 变量名
-r:将变量设置为只读变量;
-i:将变量定义为整数型变量;
-a:将变量定义为数组;
-f:显示此脚本前定义过的所有函数名及其内容;
-F:仅显示此脚本前定义过的所有函数名;
-x:将变量声明为环境变量;
-l:将变量值转为小写字母;
-u:将变量值转换为大写字母;
间接变量引用:
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值,就称为间接变量引用;
variable1=variable2
variable2=value
分析:variable1的值时variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过variable1获得变量值value的行为;
bash shell提供了两种格式实现间接变量引用;
eval tempvar=\$$variable1
tempvar=${!variabe1}
示例:
[root@centos7 testdir]# v1=v2
[root@centos7 testdir]# v2=mage
[root@centos7 testdir]# echo ${!v1}
mage
[root@centos7 testdir]#
或
[root@centos7 testdir]# v1=v2
[root@centos7 testdir]# v2=mage
[root@centos7 testdir]# eval v3=\$$v1
[root@centos7 testdir]# echo $v3
mage
[root@centos7 testdir]#
eval命令:
eval命令将会首先扫描命令行进行所有的置换,然后在执行该命令。该命令适用于那些一次扫描无法实现其功能的变量。该命令对变量进行两次扫描;
(eval命令:先将变量替换成命令,然后在执行该命令)
示例:
[root@centos7 testdir]# host=hostname
[root@centos7 testdir]# eval $host
centos7
[root@centos7 testdir]#
创建临时文件:
mktemp命令:创建的临时文件可以避免冲突,文件名后要至少跟三个大写的XXX;
mktemp [options] [TEMPLATE]
-d:创建临时目录;
–tmpdir=/DIR :指明临时文件所存放的目录位置;
示例:
创建临时指定目录下的文件:
root@centos7 testdir]# mktemp –tmpdir=/testdir test.XXX
/testdir/test.7WV
[root@centos7 testdir]#
创建临时目录:
[root@centos7 testdir]# mktemp -d dir.XXX
dir.lKU
[root@centos7 testdir]#
安装复制文件:
install命令:
install [options] source directory
-m:mode设置权限;默认755
-o:owner 设置属主;
-g:group 设置属组;
bash如何展开命令行:
把命令行分成单个命令词;
展开别名;
展开大括号中的声明({});
展开波浪符声明({});
命令替换$()和 “;
再次把命令行分成命令词;
展开文件通配(* ? [abc]等等);
准备I/O重定向(< >);
运行命令;
防止扩展:
反斜线(\)会使随后的字符按愿意解释
加引号来防止扩展
单引号防止所有的扩展;
双引号也防止所有的扩展,但是以下情况例外:
$ ——变量扩展
`——–命令替换
\——–禁止单个字符扩展
!——–历史命令替换
bash的配置文件:
按生效范围划分,可分为两类:
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
shell登录两种方式:
交互式登录:
(1)直接通过终端输入用户名密码登录;
(2)使用 su – username 切换的用户;
交互式登录系统依次所执行的文件:
/etc/profile—–>/etc/profile.d/*.sh——->~/.bash_profile——>~/.bashrc——>/etc/bashrc
非交互式登录:
(1)su username;
(2)图形界面下打开终端;
(3)执行脚本;
非交互式登录系统依次所执行的文件:
~/.bashrc—–>/etc/bashrc——–>/etc/profile.d/*.sh
profile类文件:
profile类:为交互式登录的shell提供配置:
全局:/etc/profile /etc/profile.d/*.sh
个人:~/.bash_profile
功能:
(1)用于定义环境变量;
(2)运行命令或脚本;
bashrc类文件:
bashrc类:为非交互式和交互式登录的shell提供配置:
全局:/etc/bashrc
个人:~/.bashrc
功能:
(1)定义本地变量
(2)定义命令别名和函数
编辑配置文件生效:
修改profile和bashrc文件后需要生效
两种生效方法:
(1)重新启动shell进程;
(2)使用 . 或 source 使配置文件重新加载;
bash 退出任务:
当退出登录的shell时系统会执行~/.bash_logout文件中的内容
功能:
(1)可以创建自动备份;
(2)删除临时文件;
原创文章,作者:zhengyibo,如若转载,请注明出处:http://www.178linux.com/42395