shell入门

简介

shell脚本

日常运维工作大部分是一些繁琐的重复的工作,每天80%的工作就是重复干昨天的事情,而且工作量还比较大,这就可以让机器操作这些工作。

一个简单脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装Nginx的脚本
vim install_nginx.sh

# 内容如下
yum -y install wget gcc pcre-devel zlib-devel
wget http://nginx.org/download/nginx-1.16.0.tar.gz
tar xf nginx-1.16.0.tar.gz
cd nginx-1.16.0
./configure --prefix=/usr/local/nginx
make
make install

# 执行
chmod 700 install_nginx.sh
./install_nginx.sh

脚本规范

  • 不要写中文,就算注释也最好不要中文

  • #代表注释,#!不是注释,是表示运行的环境

  • 脚本最好标明作者、创建时间、描述

  • 脚本组成

    • 解释环境 #!/usr/bin/env bash|python|perl
    • 注释说明
    • 执行代码

运行脚本:

  • 给执行权限chmod 700 install_nginx.sh,然后./install_nginx.sh
  • 解释器直接运行,不需要给权限,直接bash install_nginx.sh

shell语法

shell中的特殊符号

1
2
3
4
5
6
7
8
9
10
11
12
13
~:          家目录  # cd ~代表进入用户家目录
!: 执行历史命令!!执行上一条命令
$: 变量中取内容符
+ - * 、 %: 对应数学运算加减乘除取余数
&: 后台执行
*: 星号是shel1中的通配符匹配所有
?: 问号是shell中的通配符―匹配除回车以外的一个字符
;: 分号可以在shell中一行执行多个命令,命令之间用分号分割
|: 管道符上一个命令的输出作为下一个命令的输入cat filename l grep "abc"
\: 转义字符
``: 反引号 命令中执行命令 echo "today is `date +%F`"
' ': 单引号,脚本中字符串要用单引号引起来,但是不同于双引号的是,单引号不解释变量
" ": 双引号,脚本中出现的字符串可以用双引号引起来

管道

| 管道符在shell中使用时最多的,很多组合命令都需要通过组合命令来完成输出。管道符其实就是下一个命令对上一个命令的输出做处理

shell重定向

1
2
3
4
>     重定向输入 覆盖原数据
>> 重定向追加输入,在原数据的末尾添加
< 重定向输出 wc -l < /etc/passwd
<< 重定向追加输出 fdisk /dev/sdb <<EOF ...... EOF

shell数学运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
expr命令:只能做整数运算,格式比较古板,注意空格
[root@VM-24-12-centos ~]# expr 1 + 1
2
[root@VM-24-12-centos ~]# expr 5 - 1
4
[root@VM-24-12-centos ~]# expr 5 \* 2 # 注意*出现应该转义,否则认为是通配符
10
[root@VM-24-12-centos ~]# expr 5 / 2
2
[root@VM-24-12-centos ~]# expr 5 % 2
1

使用bc计算器处理浮点运算,scale=2代表小数点保留两位
[root@VM-24-12-centos ~]# echo "scale=2;3+100"|bc
103
[root@VM-24-12-centos ~]# echo "scale=2;100-3"|bc
97
[root@VM-24-12-centos ~]# echo "scale=2;100/3"|bc
33.33
[root@VM-24-12-centos ~]# echo "scale=2;100*3"|bc
300

或者使用bc程序交互计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@VM-24-12-centos ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1+1
2
10-2
8
3*5
15
15/2
7

scale=2
100/3
33.33

脚本方式

1
2
3
4
5
6
[root@VM-24-12-centos ~]# echo "scale=2;141*100/7966"|bc
1.77
[root@VM-24-12-centos ~]# echo "`echo "scale=2;141*100/7966"|bc`%"
1.77%
[root@VM-24-12-centos ~]# echo "当前内存使用率:`echo "scale=2;141*100/7966"|bc`%"
当前内存使用率:1.77%

双小圆括号运算,在shell中(())也可以用来做数学运算

1
2
3
4
5
6
7
8
9
10
[root@VM-24-12-centos ~]# echo $((100+3))
103
[root@VM-24-12-centos ~]# echo $((100-3))
97
[root@VM-24-12-centos ~]# echo $((100/3))
33
[root@VM-24-12-centos ~]# echo $((100%3))
1
[root@VM-24-12-centos ~]# echo $((100**3))
1000000

格式化输出echo

echo

shell基本输入

read命令

变量

在编程中,我们总有一些数据需要临时存放在内存,以待后续使用时快速读出。内存在系统启动的时候被按照1B一个单位划分为若干个块,然后统一编号(16进制编号),并对内存的使用情况做记录,保存在内存跟踪表中。

变量分类:

1.本地变量︰用户私有变量,只有本用户可以使用,保存在家目录下的.bash_profile、.bashrc文件中

2.全局变量:所有用户都可以使用,保存在/etc/profile、letc/bashrc文件中

3.用户自定义变量:用户自定义,比如脚本中的变量

定义变量:

变量格式:变量名=值

在shell编程中的变量名和等号之间不能有空格

1
2
3
4
5
变量名命名规则:
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
中间不能有空格,可以使用下划线(_)。
不能使用标点符号。
不能使用bash里的关键字(可用help命令查看保留关键字)。

读取变量内容:

读取变量内容符:$

读取方法:$变量名

取消变量unset:

unset name

定义全局变量export:

export name=‘codechen’

上述设置的变量其实都是一次性变量,系统重启就会丢失。

如果希望本地变量或者全局变量可以永久使用,可以将需要设置的变量写入变量文件中即可。

定义永久变量:

本地变量:用户私有变量,只有本用户可以使用,保存在家目录下的.bash_profile、.bashrc文件中

全局变量:所有用户都可以使用,保存在/etc/profile、letc/bashrc文件中

1
2
3
4
5
6
7
本地变量
[root@www ~]#tail -1 ~/.bash_profile
name='codechen'

全局变量
[root@www ~]# tail -1 /etc/profile
export age=30

数组

语法:

1
数组名称=(元素1 元素2 元素3 ...)

数组读出

1
2
${数组名称[索引]}
索引默认是元素在数组中的排队编号,默认第一个从0开始

数组赋值

方法一:一次赋一个值

1
2
3
array0[0]='tom'
array0[1]='jerry'
array0[2]='marry'

方法二:一次赋多个值

1
2
3
4
array2=(tom jack alice)
array3=(`cat /etc/passwd`) # 希望是将该文件中的每行作为一个元素赋值给数组array3
array4=(`ls /var/ftp/Shell/for*`)
array5=(tom jack alice "bash shell")

查看数组

1
2
3
# declare -a
declare -a array1='([0]="pear" [1]="apple" [2]="orange" [3]="peach")'
declare -a array2='([0]="pear" [1]="jack" [2]="alice")'

访问数组元素

1
2
3
4
5
6
echo ${array1[0]}       # 访问数组中的第一元素
echo ${array1[@]} # 访问数组中所有元素等同于echo ${array1[*]}
echo ${#array1[@]} # 统计数组元素的个数
echo ${!array1[@]} # 获取数组元素的索引
echo ${array1[@]:1} # 从数组小标1开始
echo ${#array1[@]:1:2} # 从数组小标1开始,访问两个元素

遍历数组

  • 默认数组通过数组元素的个数进行遍历

  • 针对关联数组可以通过数组元素的索引进行遍历

关联数组

关联数组可以允许用户自定义数组的索引,这样使用起来更加方便、高效

定义关联数组

申明关联数组变量

1
2
declare -A ass_array1
declare -A ass_array2

关联数组赋值

方法一:一次赋一个值

1
2
3
4
5
# 数组名[索引]=变量值
ass_array1[index1]=peer
ass_array1[index2]=apple
ass_array1[index3]=orange
ass_array1[index4]=peach

方法二:一次赋多个值

1
ass_array2=([index1]=tom [index2]=jack [index3]=alice [index4]='bash shell')

查看数组

1
2
3
# declare -A
declare -A ass_array1='([lindex4]="peach" [index1]="pear" [index2]="apple" [index3]="orange")"
declare -A ass_array2='([Iindex4]="bash shell" [index1]="tom" [index2]="jack" [index3]="alice")'

访问数组元素

1
2
3
4
echo ${ass_array2[index2]}   # 访问数组中的第二个元数
echo ${ass_array2[@]} # 访问数组中所有元数等同于 echo ${array1[*]}
echo ${#ass_array2[@]} # 获得数组元数的个数
echo ${!ass_array2[@]} # 获得数组元数的索引

遍历数组

通过数组元数的索引进行遍历,针对关联数组可以通过数组元素的索引进行遍历

shell流程控制-if判断语句

一、shell中的运算

数学比较运算

1
2
3
4
5
6
-eq   # 等于
-gt # 大于
-lt # 小于
-ge # 大于或等于
-le # 小于或等于
-ne # 不等于

字符串比较运算

1
2
3
4
5
# 运算符解释,注意字符串一定别忘了使用引号引起来
== # 等于
!= # 不等于
-n # 检查字符串的长度是否大于e
-z # 检查字符串的长度是否为e

文件比较检查

1
2
3
4
5
6
7
8
9
10
11
-d   # 检查文件是否存在且为目录
-e # 检查文件是否存在
-f # 检查文件是否存在且为文件
-r # 检查文件是否存在且可读
-s # 检查文件是否存在且不为空
-w # 检查文件是否存在且可写
-x # 检查文件是否存在且可执行
-o # 检查文件是否存在并且被当前用户拥有
-G # 检查文件是否存在并且默认组为当前用户组
file1 -nt file2 # 检查file1是否比file2新
file1 -ot file2 # 检查file1是否比file2日

逻辑运算

1
2
3
&&   # 逻辑与运算
|| # 逻辑或运算
! # 逻辑非运算

赋值运算

1
=   # 赋值运算符   a=10   name='baism'

if语句

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#
if [ condition ]
then
commands
fi

#
if [ condition ]
then
commands
else
commands
fi

#
if [ condition ]
then
commands
elif [ condition ]
then
commands
else
commands
fi

shell流程控制-for循环语句

语法一:直接赋值

1
2
3
4
for var in value1 value2 ......
do
commands
done

语法二:命令赋值

1
for var in `seq 1 9`

语法三:赋值是一个字符串

1
2
3
4
for var in baism\'s is cool, baism\'s is nice
do
echo "word: $var"
done

循环控制语句

sleep N脚本执行到该步休眠N秒

1
2
3
4
5
6
7
echo -n"倒计时: "
for i in 'seq 9 -1 1`
do
echo -n -e " \b$i"
sleep 1
done
echo

continue跳过循环中的某次循环

1
2
3
4
5
6
7
8
9
for ((i=1;i<10;i++))
do
if [ $i -eq 5 ]
then
continue
else
echo $i
fi
done

break跳出循环继续执行后续代码

1
2
3
4
5
6
7
8
for i in 'seq 1 9`
do
echo $i
if [ $i -eq 5 ]
then
break
fi
done

案例分享–学员信息系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

for ((i=0;i<3;i++))
do
read -p "输入第$((i + 1))个人名:" name[$i]
read -p "输入第$[$i + 1]个年龄:" age[$i]
read -p "输入第`expr $i + 1`个性别:" gender[$i]
done

clear
echo -e "\t\t\t\t学员查询系统"
while :
do
cp=0
read -p "输入要查询的姓名:" xm
[ $xm == "Q" ]&&exit
for ((i=0;i<3;i++))
do
if [ "$xm" == "${name[$i]}" ];then
echo "${name[$i]} ${age[$i]} ${gender[$i]}"
cp=1
fi
done

[ $cp -eq 0 ]&&echo "not found student"
done