学习Shell编程对于一个运维来说至关重要,现在岗位上基本都要求运维,懂1门脚本语言,或是Shell或是python,今天我们就展开Shell编程的讲解,这也是运维知识高级篇的第一篇,带大家从基础运维向中级运维转变。
Shell的作用
1. 1、安装操作系统可以选择自动安装(无人值守安装软件cobbler、kickstart) 底层调用shell脚本
2. 2、优化操作系统(防火墙优化 selinux关闭 SSH优化 加大文件描述符 修改默认YUM仓库 EPEL仓库 时间同步 关闭NetworkManager 安装常用软件 内核参数优化... ) 写入到脚本文件中
3. 3、安装部署服务(Nginx Apache PHP MySQL Redis Tomcat....)写入脚本
4. 4、业务数据统计(Linux命令) 统计好后可以定时发到领导邮箱,写入脚本+定时任务
5. 5、大文件切割(防止日志文件过大) 写成shell脚本
6. 6、辅助公司自研的(不成熟的)程序正常运行
7. 7、监控 脚本取值+定时任务-->发送到邮箱
8. 8、系统底层服务都是使用shell脚本,自研发程序或py程序
9. 9、简化操作步骤
10.
11. vim start_py.sh
12. nohup python3.5 test.py -redis-ip=172.16.1.51 -redis-port=6379 xxx &
13. vim stop_py.sh
14. ps axu|grep test.py|grep -v grep|awk '{print $2}'|xargs kill -9
学习Shell编程必备知识
1. 1、Linux基础命令,重点关注awk sed grep
2. 2、正则表达式
3. 3、熟练使用vim(各种快捷键)
4. 4、Xshell远程连接工具
如何学好Shell编程
1. 1、内容不多,主要是变量 比较表达式 if判断 for循环 while循环 流程控制语句 case语句 函数function 数组等等
2. 2、先可以读懂别人的代码,进行简单的修改代码,能够独立编写简单的脚本,一开始可以先用命令堆积(熟练后使用语句)
3. 3、多看案例,多看其他人编写的脚本,学习他人思路,变成自己思路
4. 4、切忌拿来既用
5. 5、代码的每行不是特别的理解的、使用中文注释
6. 6、有一本适合自己的教材或者是详细的笔记
7. 7、要有编程思维(来源于生活)
Shell入门
一、什么是Shell
Shell是命令解释器,Linux默认的Shell是: bash
Shell分交互式与非交互式
交互式:输入命令可以得到响应
非交互式:通过脚本运行Shell命令,无需等待用户输入命令
将基础命令写入到文件中称为shell脚本吗,文本中包含变量,判断语句,循环,数组等内容
Shell脚本书写方式
1. 1、Shell脚本存放在固定目录
2. 2、Shell脚本开头需要写解释器,默认使用的是bash
3. 3、Shell脚本必须以.sh结尾
4. 4、Shell脚本中最好不用中文做注释
5. 5、Shell中成对的符号一次性书写完毕
6. 6、Shell中语法一次书写完毕
7. 7、切记不使用中文符号
第一个Shell脚本
一、创建一个shell脚本
1. [root@LB00 ~]# mkdir -p /server/scripts/Day01
2. [root@LB00 ~]# cd /server/scripts/Day01
3. [root@LB00 Day01]# cat test.sh
4. #!/bin/bash
5. echo "Hello World"
二、执行shell脚本的三种方式
1. 第一种方式: 使用解释器执行脚本中的命令 在子shell中执行
2. [root@LB00 Day01]# sh test.sh
3. Hello World
4. [root@LB00 Day01]# bash test.sh
5. Hello World
6.
7. 第二种方式: 使用路径的方式执行脚本 必须加x执行权限 在子shell中执行,分相对路径,绝对路径
8. [root@LB00 Day01]# chmod +x test.sh #增加了x权限,下面在敲路径时候才能用tab补齐
9. [root@LB00 Day01]# ./test.sh
10. Hello World
11. [root@LB00 Day01]# /server/scripts/Day01/test.sh
12. Hello World
13.
14. 第三种执行方式: 使用source或者.执行 在父shell中执行
15. [root@LB00 Day01]# source test.sh
16. Hello World
17. [root@LB00 Day01]# . test.sh
18. Hello World
19.
20. 其他shell执行方式:
21. [root@LB00 Day01]# cat test.sh |bash
22. Hello World
23. [root@LB00 Day01]# bash < test.sh
24. Hello World
变量
一、变量的含义
1. 用一个固定不变的值来代替经常变化的值称为变量!
2. x=1 y=x+1 y=2
3. 类似书本的目录: 目录存放着具体内容
二、变量的分类
1. 环境变量(全局变量) 对于系统所有的bash生效 默认系统定义好的 都是大写
2. 普通变量(局部变量) 自己来定义的变量 一般都在脚本文件中定义
3. 全局变量先生效,局部变量后生效,所以全局变量与局部变量重复时候,最终局部变量生效
4.
5. 按照生成周期分类:
6. 临时变量: 直接在当前shell中定义的变量 重启或者重新连接失效 name=oldboy
7. 永久变量: 写入到/etc/profile(每次xshell连接都会执行里面的变量)
8.
9. 定义变量的两种书写方式:
10. 变量名称=变量的值 只在当前的shell中生效
11. export 变量名称=变量的值 在所有的shell中生效,但是其他连接的会话不会生效

三、变量相关的配置文件
1. 全局生效
2. [root@lb day01]# ll /etc/profile
3. -rw-r--r-- 1 root root 1819 May 12 12:10 /etc/profile
4. 用户变量文件:
5. ~/.bashrc
6. ~/.bash_profile
7. /etc/bashrc
8.
9. shell环境变量相关配置文件的优先级是(执行顺序同)
10. /etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc
四、变量定义方式
1. 语法:
2. 变量名称=变量的值
3. 变量名称的定义方式: 字母 数字 下划线组合,以字母或者下划线开头,不能以数字开头,等号两端不允许有空格
4.
5. 变量名称的书写方式: 见名知其意
6. 第一种: 全大写 NAME_AGE 系统默认都是大写的
7. 第二种: 全小写 name_age
8. 第三种: 首字母大写 大驼峰语法 Name_Age
9. 第四种:首字母大写,但是首单词不大写 小驼峰语法 name_Age
10.
11. 变量值的定义:
12.
13. 第一种: 数字的定义,连续的数字,不连续的使用引号引起来
14. [root@LB00 ~]# age=123
15. [root@LB00 ~]# age=12 3
16. -bash: 3: command not found
17. [root@LB00 ~]# age='12 3'
18. [root@LB00 ~]# echo $age
19. 12 3
20.
21. 第二种: 字符串定义,连续的字符串
22. [root@LB00 ~]# name=ko ten #字符串
23. -bash: ten: command not found
24. [root@LB00 ~]# name='ko ten'
25. [root@LB00 ~]# echo $name
26. ko ten
27. [root@LB00 ~]# dir=/etc/sysconfig/network-scripts/ifcfg-eth0 #文件路径
28. [root@LB00 ~]# echo $dir
29. /etc/sysconfig/network-scripts/ifcfg-eth0
30. [root@LB00 ~]# cat $dir
31. TYPE=Ethernet
32. BOOTPROTO=none
33. NAME=eth0
34. DEVICE=eth0
35. ONBOOT=yes
36. IPADDR=10.0.0.4
37. PREFIX=24
38. GATEWAY=10.0.0.2
39. DNS1=223.5.5.5
40. [root@LB00 ~]# dir=/server/scripts/ #目录路径
41. [root@LB00 ~]# cd $dir
42. [root@LB00 scripts]# cd
43. [root@LB00 ~]# tar zcvf all.tar.gz $dir
44. tar: Removing leading `/' from member names
45. /server/scripts/
46. /server/scripts/Day01/
47. /server/scripts/Day01/test.sh
48.
49. 第三种定义方式: 命令的定义
50. [root@LB00 ~]# test=`pwd` #命令执行后将执行结果发送给变量,而不是每调用一次就执行一次
51. [root@LB00 ~]# echo $test
52. /root
53. [root@LB00 ~]# cd /
54. [root@LB00 /]# echo $test
55. /root
56. [root@LB00 ~]# test='pwd' #将命令赋值给变量
57. [root@LB00 ~]# echo $test
58. pwd
59. [root@LB00 ~]# $test #这个才是执行变量
60. /root
61. [root@LB00 ~]# cd /
62. [root@LB00 /]# $test
63. /
64. 下面同理,注意是想命令执行后给变量还是把命令给变量
65. [root@LB00 /]# test=`date +%F-%H-%M-%S`
66. [root@LB00 /]# echo $test
67. 2023-05-12-18-22-11
68. [root@LB00 /]# echo $test
69. 2023-05-12-18-22-11
70. [root@LB00 /]# test='date +%F-%H-%M-%S'
71. [root@LB00 /]# echo $test
72. date +%F-%H-%M-%S
73. [root@LB00 /]# $test
74. 2023-05-12-18-22-57
75. [root@LB00 /]# $test
76. 2023-05-12-18-22-58
还有一个进阶用法,一般在脚本内部使用,命令行不使用这个,就是变量如果没有被定义,会有默认值
1. [root@Master231 ~]# cat name.sh
2. NAME=${1:-koten}
3. echo $NAME
4. [root@Master231 ~]# sh name.sh
5. koten
6. [root@Master231 ~]# sh name.sh boss
7. boss
五、Shell重要的位置变量
1. 5.Shell重要的位置变量
2.
3. $0 表示脚本的名称
4. [root@LB00 Day01]# cat test.sh
5. #!/bin/bash
6. echo $0
7. [root@LB00 Day01]# sh test.sh
8. test.sh
9.
10. 使用场景:
11. [root@LB00 Day01]# cat test.sh
12. #!/bin/bash
13. echo "Usage: $0 {start|stop|status|restart|force-reload}"
14. [root@LB00 Day01]# sh test.sh
15. Usage: test.sh {start|stop|status|restart|force-reload}
16. [root@LB00 Day01]# /server/scripts/Day01/test.sh
17. Usage: /server/scripts/Day01/test.sh {start|stop|status|restart|force-reload}
18.
19. $n n为数字 表示脚本的第n个参数 0被名称占用 所以从1开始
20. $1 为脚本第一个参数 $2 为脚本的第二个参数
21. [root@LB00 Day01]# cat test.sh
22. #!/bin/bash
23. echo $1 $2
24. [root@LB00 Day01]# sh test.sh name 20
25. name 20
26. [root@LB00 Day01]# cat test.sh
27. #!/bin/bash
28. echo $1 $2 ${10} ${11}
29. [root@LB00 Day01]# sh test.sh {a..z}
30. a b j k
31.
32. $# 表示脚本传参的个数
33. [root@LB00 Day01]# cat test.sh
34. #!/bin/bash
35. echo $#
36. [root@LB00 Day01]# sh test.sh {a..z}
37. 26
38. 使用场景: 控制用户传参的个数
39. [root@LB00 Day01]# cat test.sh
40. #!/bin/bash
41. if [ $# -ne 2 ]
42. then
43. echo "必须输入两个参数"
44. exit
45. fi
46. echo $1+$2|bc
47. [root@LB00 Day01]# sh test.sh 1 1
48. 2
49.
50. $? 表示上一条命令的执行结果 0为成功 非0失败
51. 使用场景:
52. [root@LB00 Day01]# cat install.sh
53. #!/bin/bash
54. yum -y install wget &> /dev/null
55. [ $? -eq 0 ] && echo "wget安装成功" || echo "安装失败"
56. [root@LB00 Day01]# sh install.sh
57. wget安装成功
58.
59. [root@LB00 Day01]# cat ping.sh
60. #!/bin/bash
61. ping -c1 -W1 $1 &>/dev/null
62. [ $? -eq 0 ] && echo "$1 ping的通" || "$1 ping不通"
63. [root@LB00 Day01]# sh ping.sh www.baidu.com
64. www.baidu.com ping的通
65.
66. $$ 表示脚本的PID
67. [root@LB00 Day01]# cat test.sh
68. #!/bin/bash
69. echo $$ > /tmp/nginx.pid #方便到时候删除
70.
71. $* 获取脚本的所有参数 在循环体中加双引号把所有的参数作为一个参数
72. [root@LB00 Day01]# set -- "a b" c
73. [root@LB00 Day01]# echo $*
74. a b c
75. [root@LB00 Day01]# for i in $*;do echo $i;done
76. a
77. b
78. c
79. [root@LB00 Day01]# for i in "$*";do echo $i;done
80. a b c
81.
82. $@ 获取脚本的所有参数 在循环体中加上引号把所有的参数作为独立的参数
83. [root@LB00 Day01]# for i in "$@";do echo $i;done
84. a b
85. c
86.
87. $_ 表示获取执行脚本最后的一个值 类似esc .
88.
89.
90. -----重要
91. $0 表示脚本名称
92. $n 表示脚本传参的第n个参数
93. $# 表示传参的个数
94. $? 表示上一条命令的执行结果
95.
96. ------了解
97. $$ 脚本pid
98. $! 表示上一个后台任务(即通过&符号向后台提交的进程)的进程ID
99. $* 获取脚本的所有参数,在循环体中加双引号,把所有参数作为一个参数整体
100. $@ 获取脚本的所有参数,在循环体中加双引号,所有参数都是独立的参数
101. $_ 脚本中上一条命令的最后的参数,类似于esc .
六、变量三种传参方式
1. 第一种: 直接传参
2. [root@LB00 Day01]# cat test.sh
3. #!/bin/bash
4. echo name: $1
5. echo age: $2
6. [root@LB00 Day01]# sh test.sh koten 20
7. name: koten
8. age: 20
9.
10. 第二种: 赋值传参
11. [root@LB00 Day01]# cat test.sh
12. #!/bin/bash
13. name=$1
14. age=$2
15. echo name: $name
16. echo age: $age
17. [root@LB00 Day01]# sh test.sh koten 20
18. name: koten
19. age: 20
20.
21. 第三种: read读入,交互式
22. [root@LB00 Day01]# cat test.sh
23. #!/bin/bash
24. read -p "请您输入姓名:" name
25. read -p "请您输入年龄:" age
26. echo 姓名: $name
27. echo 年龄: $age
28. [root@LB00 Day01]# sh test.sh
29. 请您输入姓名:koten
30. 请您输入年龄:20
31. 姓名: koten
32. 年龄: 20
扩展
需求:使用三种传参方式,修改主机名称和IP地址,修改完IP,输出修改的结果到屏幕,在重启网络
一、直接传参
1. [root@LB00 Day01]# cat change.sh
2. #!/bin/bash
3. hostnamectl set-hostname --static $1
4. sed -i 's#IPADDR.*#IPADDR='$2'#g' /etc/sysconfig/network-scripts/ifcfg-eth0
5. systemctl restart network
6. [root@LB00 Day01]# sh change.sh LB00 10.0.0.4
7. [root@LB00 Day01]# hostname
8. LB00
9. [root@LB00 Day01]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
10. TYPE=Ethernet
11. BOOTPROTO=none
12. NAME=eth0
13. DEVICE=eth0
14. ONBOOT=yes
15. IPADDR=10.0.0.4
16. PREFIX=24
17. GATEWAY=10.0.0.2
18. DNS1=223.5.5.5
二、赋值传参
1. [root@LB00 Day01]# cat change.sh
2. #!/bin/bash
3. hostname=$1
4. ip=$2
5. hostnamectl set-hostname --static $hostname
6. sed -i 's#IPADDR.*#IPADDR='$ip'#g' /etc/sysconfig/network-scripts/ifcfg-eth0
7. systemctl restart network
三、read交互式传参
1. [root@LB00 Day01]# cat change.sh
2. #!/bin/bash
3. read -p "请输入要修改的主机名:" hostname
4. read -p "请输入要修改的ip:" ip
5. hostnamectl set-hostname --static $hostname
6. sed -i 's#IPADDR.*#IPADDR='$ip'#g' /etc/sysconfig/network-scripts/ifcfg-eth0
7. systemctl restart network
8. [root@LB00 Day01]# sh change.sh
9. 请输入要修改的主机名:LB0
10. 请输入要修改的ip:10.0.0.3
11.
12. #因为修改了ip,Xshell断开了

我是koten,10年运维经验,持续分享运维干货,感谢大家的阅读和关注!