SHELL

Shell脚本编程

1.shell程序编程风格

面向过程语言

多一件事,排出个步骤,第一步干什么,第二步干什么,如果出现情况A,做什么处理,如果出现了情况B,做什么处理

问题规模小,可以步骤化,按部就班处理

以指令为中心,数据服务于指令

2.脚本创建过程

第一步:使用文本编辑器来创建文本文件

第一行必须包括shell声明序列: #!

#!/bin/bash

添加注释,注释以#开头

第二步:加执行权限

给予执行权限,在命令行上指定脚本的绝对或相对路径

第三步:运行脚本

直接运行解释器,将脚本作为解释器程序的参数运行

3.第一个脚本hello world

[21:25:28 root@aiyoubucuo ~]$cat /data/ybw.sh 
#!/bin/bash
echo "hello world"

 [21:28:30 root@aiyoubucuo ~]$. /data/ybw.sh
  hello world

4.shell脚本调试

只检测脚本中语法错误,但无法检查出命令错误,但不真正执行脚本

bash -n /data/ybw.sh

调试并执行

bash -x /data/ybw.sh

5.Shell变量类型

内置变量,如:PS1,PATH,UID,HOSTNAME,BASHPID

用户自定义变量

不同的变量存放的数据不同,决定了以下

数据存储方式

参与的运算

表示的数据范围

6.变量的使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可

your="ybw"
echo $your
echo ${your_name}

#变量名外面的{花括号}是可选的,为了帮助解释器识别变量的边界

7.只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

下面的例子尝试更改只读变量,结果报错:

#!/bin/bash
your="jommy"
readonly your
your="zxx"

运行脚本结果:

[root@localhost ~]# ./2.sh 
./2.sh: line 4: your: readonly variable

8.删除变量

使用 unset 命令删除变量

unset jommy

变量被删除后不能再次使用。unset 命令不能删除只读变量。

#!/bin/bash
your="ybw"
unset your
echo $your

以上运行没有任何输出结果

9.位置变量

位置变量:在bash shell中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数

 $1,$2 ... 对应第1个,第2个等参数,shift【n】换位置

$0 命令本身,包括路径

$* 传递给脚本的所有参数,全部参数整合为一个字符串

$@ 传递给脚本的所有参数,每个参数为独立字符串

$# 传递给脚本的参数的个数

echo "1st arg is $1"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${10}"
echo "11st arg is ${11}"

echo "the number of arg is $#"
echo "all args are $*"
echo "all args are $@"
echo "the scriptime is `basename $0`"
[13:33:22 root@aiyoubucuo ~]./arg.sh {a..z}
1st arg is a
2st arg is b
3st arg is c
10st arg is j
11st arg is k
the number of arg is 26
all args are a b c d e f g h i j k l m n o p q r s t u v w x y z
all args are a b c d e f g h i j k l m n o p q r s t u v w x y z
the scriptime is arg.sh

 10.退出状态码变量

$?的值为0 代表成功

$?的值是1-255 代表失败

[20:03:49 root@aiyoubucuo ~]curl http://www.baidu.com
[20:04:02 root@aiyoubucuo ~]$?
0: command not found
访问百度结果是0
[20:04:11 root@aiyoubucuo ~]curl http://www.sdhfuiashdncieow.com
curl: (6) Could not resolve host: www.sdhfuiashdncieow.com
[20:04:43 root@aiyoubucuo ~]$?
6: command not found
瞎输入一个结果是6

用户可以在脚本中使用以下命令自定义退出状态码

exit [n]

脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字

如果为给脚本退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

11.脚本安全和set

set命令可以来定制shell环境

-u 在扩展一个没有设置的变量时,显示错误信息

-e 如果一个命令返回一个非0退出状态值(失败)就退出

可以一起使用set -eu,但输出结果显示错误就退出,有时候不是那种真正的错误也会退出

12.格式化输出printf

printf “指定的格式” 文本1 文本2

%s:字符串

%f:浮点格式

常用转义字符

:换行

f:换页

:后退

[20:31:59 root@aiyoubucuo ~]printf "(%s)
" 1 2 3
(1)
(2)
(3)

13.算数运算

bash只支持整数,不支持小数

var=$((算术表达式))

var=$[算数表达式]

$RANDOM 取值范围0-32767

 14.逻辑运算

1,真

2,假

以上为二进制

[20:50:37 root@aiyoubucuo ~]true
[21:06:40 root@aiyoubucuo ~]echo $?
0
[21:06:49 root@aiyoubucuo ~]false
[21:06:56 root@aiyoubucuo ~]echo $?
1

短路与

CMD1 短路与 CMD2

第一个CMD1结果为真,第二个CMD2必须要参与运算,才能得到最终结果

第一个CMD1结果为假,总的结果必定为0,因此不需要执行CMD2

也可以用&&表示短路与

命令1成功执行命令2,否则不执行命令2

短路或

CMD1 短路或 CMD2

第一个CMD1结果为真,总的结果必定为0,因此不需要执行CMD2

第一个CMD1结果为假,第二个CMD2必须要参与运算,才能得到最终结果

也可以用||表示短路或

命令1成功将不执行命令2,否则将执行命令2

&&和||组合使用

 如果有wang账户则执行echo命令,没有则执行创建用户命令

 15.条件测试命令

若真,则状态码变量$?返回0

若假,则状态码变量$?返回1

test EXPRESSION

[EXPRESSION]和test等价

[[EXPRESSION]]用法

=~ 左侧字符串是否能够被右侧的征兆表达式匹配,支持正则表达式

[21:25:06 root@aiyoubucuo ~]#[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]
|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]

[21:27:45 root@aiyoubucuo ~]#echo $?
1
判断ip是否合法

16.数值测试

-eq是否大于等于

-ne是否不等于 

-gt是否大于

-ge是否大于等于

-lt是否小于

-le是否小于等于

[21:18:16 root@aiyoubucuo ~]i=10
[21:18:20 root@aiyoubucuo ~]j=8
[21:18:28 root@aiyoubucuo ~][ $i -gt $j ]
[21:19:09 root@aiyoubucuo ~]echo $?
0

17.关于()和{}

两种符号都可以将多个命令组合在一起,批量执行

(list)会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境

  {list} 不会开启子shell,在当前shell中运行,会影响当前shell环境

[21:36:58 root@aiyoubucuo ~]name=ybw;(echo $name;name=yang;echo $name);echo $name
ybw
yang
ybw
[21:37:06 root@aiyoubucuo ~]name=ybw;{ echo $name;name=yang;echo $name; } ;echo $name
ybw
yang
yang

18.read

使用read命令来接受输入

使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每一个单词分配一个变量,所有剩余单词被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋给系统内置变量reply

read 选项 【name...】

-p指定要显示的提示,常用

 19.if

单分支

if 判断条件;then

条件为真的分支代码

fi

双分支

f 判断条件;then

条件为真的分支代码

else

条件为假的分支代码

fi

多分支

if 判断条件1;then

 条件1为真的分支代码

elif 判断条件2;then

条件2为真的分支代码

elif 判断条件3;then

条件3为真的分支代码

...

else

以上条件都为假的分支代码

fi

例子

#!/bin/bash
read -p "请输入身高(m为单位):" HIGH
   if [[ ! "$HIGH" =~ ^[0-2](.[0-9]{,2})?$ ]];then
          echo "输入身高错误"
         exit 1
   fi
  read -p "请输入体重(kg为单位):" WEIGHT
   if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then
          echo "输入体重错误"
         exit 1
   fi
BMI=`echo $WEIGHT/$HIGH^2|bc`
if [ $BMI -le 18 ] ;then
        echo "太瘦了宝贝,多吃点"
elif [ $BMI -lt 24 ] ;then
        echo "身材不错,想办法追我"
else
        echo "你是猪吗宝贝"
fi

19.条件判断case语句

case支持通配符

#!/bin/bash
echo "1,备份数据库"
echo "2.清理日志"
echo "3.软件升级"
echo "4.软件回滚"
read -p "请选择上面数字1-4:" MENU case $MENU in 1) echo "备份数据" ;; 2) echo "清理日志" ;; 3) echo "软件升级" ;; 4) echo "软件回滚" esac

20.for循环

依次将列表中的元素赋值给“变量名”,每次赋值后即执行一次循环体;知道列表中的元素耗尽,循环结束

如果省略【in words】,此时使用位置参量

#!/bin/bash
sum=0
for i in {1..100}
do
let sum+=i
done
echo "sum=$sum"
for循环计算1..100

  for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))

do

循环体

#!/bin/bash
for((sum=0,i=1;i<=100;sum+=i,i++))
do
        true
done
echo $sum

 21.循环while

while commands; do commands; done

while condition; do

        循环体

done

condition:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为真,则执行一次循环;直到条件测试状态假,则终止循环,因此:condtion一般应有循环控制变量;而此变量的值在循环体不断地被修正

进入条件:condition为true

退出条件:condition为假

无限循环

while true; do

    循环体

done

while :; do

    循环体

done

#!/bin/bash
sum=0
i=1
while ((i<=100)) ;do let sum+=i;let i++
done
echo $sum

while特殊用法while read

while read line;do

          循环体

done < /path/from/somefile

说明:依次读取/path/from/somefile文件中的每一行,且将行赋值给变量line

[21:17:22 root@centos7 ~]$echo yang bo wen | while read x y z;do echo $x $y $z;done
yang bo wen

 22.循环控制语句continue

continue 【N】:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第一层

for ((i=0;i<3;i++));do
for    ((j=0;j<4;j++));do
         [ $j -eq 2 ] && continue
        echo $j
done
echo -----------------------------
done
结束内圈循环

[23:43:06 root@centos7 ~]$bash coun.sh
0
1
3
-----------------------------
0
1
3
-----------------------------
0
1
3
-----------------------------

 

continue 2结束外圈循环

for ((i=0;i<3;i++));do
for    ((j=0;j<4;j++));do
         [ $j -eq 2 ] && continue 2
        echo $j
done
echo -----------------------------
done
结束外圈循环
0
1
0
1
0
1

23.break

提前结束第N个循环

for ((i=0;i<3;i++));do
for    ((j=0;j<4;j++));do
         [ $j -eq 2 ] && break
        echo $j
done
echo -----------------------------
done
结束内圈循环
[23:51:35 root@centos7 ~]$bash coun.sh 
0
1
-----------------------------
0
1
-----------------------------
0
1
-----------------------------

break 2

for ((i=0;i<3;i++));do
for    ((j=0;j<4;j++));do
         [ $j -eq 2 ] && break 2
        echo $j
done
echo -----------------------------
done
直接结束外圈循环
0
1

24 循环与菜单select

select循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示PS3提示符等待用户输入

select是个无线循环,因此要用break命令退出循环,或用exit命令终止脚本。select经常和case联合使用。

 
[21:32:55 root@centos7 ~]$bash ybw.sh
1) 北京老鸭
2) 佛跳墙
3) 小龙虾
4) 羊蝎子
5) 火锅
6) 点才结束
请点菜(1-6):

#!/bin/bash
sum=0
PS3="请点菜(1-6):"
select MENU in 北京老鸭 佛跳墙 小龙虾 羊蝎子 火锅 点才结束;do
    case $REPLY in
(1)
echo
$MENU 价格是 100 let sum+=100 ;; (2) echo $MENU 价格是 88 let sum+=88 ;; (3) echo $MENU 价格是 66 let sum+=66 ;; (4) echo $MENU 价格是 166 let sum+=166 ;; (5) echo $MENU 价格是 200 let sum+=200 ;; (6) echo "点菜结束,退出" break ;; (*) echo "点菜错误,重新选择" ;; esac done echo "总价是:$sum"

25.函数function

函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程

它与shell程序形式上是相似的,不同的是他不是一个单独的进程,不能独立运行,而是shell程序的一部分

函数和shell程序区别

shell程序在子shell中运行

函数在当前shell中运行。因此在当前shell中,函数可对shell中变量进行修改

函数由两部分组成:函数名和函数体

declare -F
查看当前已定义的函数名
declare -f
查看当前已定义的函数定义
declare -f func_name
查看指定当前已定义的函数名
declare -F func_name
查看当前已定义的函数名定义

 删除函数

unset func_name

交互式环境调用函数

[22:13:23 root@centos7 ~]$you() {
> echo "i am Spider-Man"
> }
[22:17:14 root@centos7 ~]$you
i am Spider-Man

实现判断Centos的主版本

[22:21:57 root@centos7 ~]$kan() {
> sed -nr 's/.* ([0-9].[0-9]).*/1/p' /etc/redhat-release
> }
[22:37:02 root@centos7 ~]$kan
7.5

脚本中定义及使用函数

[22:42:11 root@centos7 ~]$bash li.sh 
now going to the function hello
hello there today is 2021-01-03
back from the function

#!/bin/bash
hello() {
    echo "hello there today is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现后才能使用,调用函数仅使用其函数名即可
source 接函数路径,输出函数名,即可调用函数
原文地址:https://www.cnblogs.com/aiyoubucuo/p/14117274.html