学习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脚本吗,文本中包含变量,判断语句,循环,数组等内容
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年运维经验,持续分享运维干货,感谢大家的阅读和关注!