学习Shell编程对于一个运维来说至关重要,现在岗位上基本都要求运维,懂1门脚本语言,或是Shell或是python,今天我们就展开Shell编程的讲解,这也是运维知识高级篇的第一篇,带大家从基础运维向中级运维转变。
目录
Shell的作用
1、安装操作系统可以选择自动安装(无人值守安装软件cobbler、kickstart) 底层调用shell脚本
2、优化操作系统(防火墙优化 selinux关闭 SSH优化 加大文件描述符 修改默认YUM仓库 EPEL仓库 时间同步 关闭NetworkManager 安装常用软件 内核参数优化... ) 写入到脚本文件中
3、安装部署服务(Nginx Apache PHP MySQL Redis Tomcat....)写入脚本
4、业务数据统计(Linux命令) 统计好后可以定时发到领导邮箱,写入脚本+定时任务
5、大文件切割(防止日志文件过大) 写成shell脚本
6、辅助公司自研的(不成熟的)程序正常运行
7、监控 脚本取值+定时任务-->发送到邮箱
8、系统底层服务都是使用shell脚本,自研发程序或py程序
9、简化操作步骤
vim start_py.sh
nohup python3.5 test.py -redis-ip=172.16.1.51 -redis-port=6379 xxx &
vim stop_py.sh
ps axu|grep test.py|grep -v grep|awk '{print $2}'|xargs kill -9
学习Shell编程必备知识
1、Linux基础命令,重点关注awk sed grep
2、正则表达式
3、熟练使用vim(各种快捷键)
4、Xshell远程连接工具
如何学好Shell编程
1、内容不多,主要是变量 比较表达式 if判断 for循环 while循环 流程控制语句 case语句 函数function 数组等等
2、先可以读懂别人的代码,进行简单的修改代码,能够独立编写简单的脚本,一开始可以先用命令堆积(熟练后使用语句)
3、多看案例,多看其他人编写的脚本,学习他人思路,变成自己思路
4、切忌拿来既用
5、代码的每行不是特别的理解的、使用中文注释
6、有一本适合自己的教材或者是详细的笔记
7、要有编程思维(来源于生活)
Shell入门
一、什么是Shell
Shell是命令解释器,Linux默认的Shell是: bash
Shell分交互式与非交互式
交互式:输入命令可以得到响应
非交互式:通过脚本运行Shell命令,无需等待用户输入命令
二、什么是Shell脚本
将基础命令写入到文件中称为shell脚本吗,文本中包含变量,判断语句,循环,数组等内容
Shell脚本书写方式
1、Shell脚本存放在固定目录
2、Shell脚本开头需要写解释器,默认使用的是bash
3、Shell脚本必须以.sh结尾
4、Shell脚本中最好不用中文做注释
5、Shell中成对的符号一次性书写完毕
6、Shell中语法一次书写完毕
7、切记不使用中文符号
第一个Shell脚本
一、创建一个shell脚本
[root@LB00 ~]# mkdir -p /server/scripts/Day01
[root@LB00 ~]# cd /server/scripts/Day01
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo "Hello World"
二、执行shell脚本的三种方式
第一种方式: 使用解释器执行脚本中的命令 在子shell中执行
[root@LB00 Day01]# sh test.sh
Hello World
[root@LB00 Day01]# bash test.sh
Hello World
第二种方式: 使用路径的方式执行脚本 必须加x执行权限 在子shell中执行,分相对路径,绝对路径
[root@LB00 Day01]# chmod +x test.sh #增加了x权限,下面在敲路径时候才能用tab补齐
[root@LB00 Day01]# ./test.sh
Hello World
[root@LB00 Day01]# /server/scripts/Day01/test.sh
Hello World
第三种执行方式: 使用source或者.执行 在父shell中执行
[root@LB00 Day01]# source test.sh
Hello World
[root@LB00 Day01]# . test.sh
Hello World
其他shell执行方式:
[root@LB00 Day01]# cat test.sh |bash
Hello World
[root@LB00 Day01]# bash < test.sh
Hello World
变量
一、变量的含义
用一个固定不变的值来代替经常变化的值称为变量!
x=1 y=x+1 y=2
类似书本的目录: 目录存放着具体内容
二、变量的分类
环境变量(全局变量) 对于系统所有的bash生效 默认系统定义好的 都是大写
普通变量(局部变量) 自己来定义的变量 一般都在脚本文件中定义
全局变量先生效,局部变量后生效,所以全局变量与局部变量重复时候,最终局部变量生效
按照生成周期分类:
临时变量: 直接在当前shell中定义的变量 重启或者重新连接失效 name=koten
永久变量: 写入到/etc/profile(每次xshell连接都会执行里面的变量)
定义变量的两种书写方式:
变量名称=变量的值 只在当前的shell中生效
export 变量名称=变量的值 在所有的shell中生效,但是其他连接的会话不会生效
三、变量相关的配置文件
全局生效
[root@lb day01]# ll /etc/profile
-rw-r--r-- 1 root root 1819 May 12 12:10 /etc/profile
用户变量文件:
~/.bashrc
~/.bash_profile
/etc/bashrc
shell环境变量相关配置文件的优先级是(执行顺序同)
/etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc
四、变量定义方式
语法:
变量名称=变量的值
变量名称的定义方式: 字母 数字 下划线组合,以字母或者下划线开头,不能以数字开头,等号两端不允许有空格
变量名称的书写方式: 见名知其意
第一种: 全大写 NAME_AGE 系统默认都是大写的
第二种: 全小写 name_age
第三种: 首字母大写 大驼峰语法 Name_Age
第四种:首字母大写,但是首单词不大写 小驼峰语法 name_Age
变量值的定义:
第一种: 数字的定义,连续的数字,不连续的使用引号引起来
[root@LB00 ~]# age=123
[root@LB00 ~]# age=12 3
-bash: 3: command not found
[root@LB00 ~]# age='12 3'
[root@LB00 ~]# echo $age
12 3
第二种: 字符串定义,连续的字符串
[root@LB00 ~]# name=ko ten #字符串
-bash: ten: command not found
[root@LB00 ~]# name='ko ten'
[root@LB00 ~]# echo $name
ko ten
[root@LB00 ~]# dir=/etc/sysconfig/network-scripts/ifcfg-eth0 #文件路径
[root@LB00 ~]# echo $dir
/etc/sysconfig/network-scripts/ifcfg-eth0
[root@LB00 ~]# cat $dir
TYPE=Ethernet
BOOTPROTO=none
NAME=eth0
DEVICE=eth0
ONBOOT=yes
IPADDR=10.0.0.4
PREFIX=24
GATEWAY=10.0.0.2
DNS1=223.5.5.5
[root@LB00 ~]# dir=/server/scripts/ #目录路径
[root@LB00 ~]# cd $dir
[root@LB00 scripts]# cd
[root@LB00 ~]# tar zcvf all.tar.gz $dir
tar: Removing leading `/' from member names
/server/scripts/
/server/scripts/Day01/
/server/scripts/Day01/test.sh
第三种定义方式: 命令的定义
[root@LB00 ~]# test=`pwd` #命令执行后将执行结果发送给变量,而不是每调用一次就执行一次
[root@LB00 ~]# echo $test
/root
[root@LB00 ~]# cd /
[root@LB00 /]# echo $test
/root
[root@LB00 ~]# test='pwd' #将命令赋值给变量
[root@LB00 ~]# echo $test
pwd
[root@LB00 ~]# $test #这个才是执行变量
/root
[root@LB00 ~]# cd /
[root@LB00 /]# $test
/
下面同理,注意是想命令执行后给变量还是把命令给变量
[root@LB00 /]# test=`date +%F-%H-%M-%S`
[root@LB00 /]# echo $test
2023-05-12-18-22-11
[root@LB00 /]# echo $test
2023-05-12-18-22-11
[root@LB00 /]# test='date +%F-%H-%M-%S'
[root@LB00 /]# echo $test
date +%F-%H-%M-%S
[root@LB00 /]# $test
2023-05-12-18-22-57
[root@LB00 /]# $test
2023-05-12-18-22-58
还有一个进阶用法,一般在脚本内部使用,命令行不使用这个,就是变量如果没有被定义,会有默认值
[root@Master231 ~]# cat name.sh
NAME=${1:-koten}
echo $NAME
[root@Master231 ~]# sh name.sh
koten
[root@Master231 ~]# sh name.sh boss
boss
五、Shell重要的位置变量
5.Shell重要的位置变量
$0 表示脚本的名称
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo $0
[root@LB00 Day01]# sh test.sh
test.sh
使用场景:
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo "Usage: $0 {start|stop|status|restart|force-reload}"
[root@LB00 Day01]# sh test.sh
Usage: test.sh {start|stop|status|restart|force-reload}
[root@LB00 Day01]# /server/scripts/Day01/test.sh
Usage: /server/scripts/Day01/test.sh {start|stop|status|restart|force-reload}
$n n为数字 表示脚本的第n个参数 0被名称占用 所以从1开始
$1 为脚本第一个参数 $2 为脚本的第二个参数
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo $1 $2
[root@LB00 Day01]# sh test.sh name 20
name 20
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo $1 $2 ${10} ${11}
[root@LB00 Day01]# sh test.sh {a..z}
a b j k
$# 表示脚本传参的个数
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo $#
[root@LB00 Day01]# sh test.sh {a..z}
26
使用场景: 控制用户传参的个数
[root@LB00 Day01]# cat test.sh
#!/bin/bash
if [ $# -ne 2 ]
then
echo "必须输入两个参数"
exit
fi
echo $1+$2|bc
[root@LB00 Day01]# sh test.sh 1 1
2
$? 表示上一条命令的执行结果 0为成功 非0失败
使用场景:
[root@LB00 Day01]# cat install.sh
#!/bin/bash
yum -y install wget &> /dev/null
[ $? -eq 0 ] && echo "wget安装成功" || echo "安装失败"
[root@LB00 Day01]# sh install.sh
wget安装成功
[root@LB00 Day01]# cat ping.sh
#!/bin/bash
ping -c1 -W1 $1 &>/dev/null
[ $? -eq 0 ] && echo "$1 ping的通" || "$1 ping不通"
[root@LB00 Day01]# sh ping.sh www.baidu.com
www.baidu.com ping的通
$$ 表示脚本的PID
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo $$ > /tmp/nginx.pid #方便到时候删除
$* 获取脚本的所有参数 在循环体中加双引号把所有的参数作为一个参数
[root@LB00 Day01]# set -- "a b" c
[root@LB00 Day01]# echo $*
a b c
[root@LB00 Day01]# for i in $*;do echo $i;done
a
b
c
[root@LB00 Day01]# for i in "$*";do echo $i;done
a b c
$@ 获取脚本的所有参数 在循环体中加上引号把所有的参数作为独立的参数
[root@LB00 Day01]# for i in "$@";do echo $i;done
a b
c
$_ 表示获取执行脚本最后的一个值 类似esc .
-----重要
$0 表示脚本名称
$n 表示脚本传参的第n个参数
$# 表示传参的个数
$? 表示上一条命令的执行结果
------了解
$$ 脚本pid
$! 表示上一个后台任务(即通过&符号向后台提交的进程)的进程ID
$* 获取脚本的所有参数,在循环体中加双引号,把所有参数作为一个参数整体
$@ 获取脚本的所有参数,在循环体中加双引号,所有参数都是独立的参数
$_ 脚本中上一条命令的最后的参数,类似于esc .
六、变量三种传参方式
第一种: 直接传参
[root@LB00 Day01]# cat test.sh
#!/bin/bash
echo name: $1
echo age: $2
[root@LB00 Day01]# sh test.sh koten 20
name: koten
age: 20
第二种: 赋值传参
[root@LB00 Day01]# cat test.sh
#!/bin/bash
name=$1
age=$2
echo name: $name
echo age: $age
[root@LB00 Day01]# sh test.sh koten 20
name: koten
age: 20
第三种: read读入,交互式
[root@LB00 Day01]# cat test.sh
#!/bin/bash
read -p "请您输入姓名:" name
read -p "请您输入年龄:" age
echo 姓名: $name
echo 年龄: $age
[root@LB00 Day01]# sh test.sh
请您输入姓名:koten
请您输入年龄:20
姓名: koten
年龄: 20
扩展
需求:使用三种传参方式,修改主机名称和IP地址,修改完IP,输出修改的结果到屏幕,在重启网络
一、直接传参
[root@LB00 Day01]# cat change.sh
#!/bin/bash
hostnamectl set-hostname --static $1
sed -i 's#IPADDR.*#IPADDR='$2'#g' /etc/sysconfig/network-scripts/ifcfg-eth0
systemctl restart network
[root@LB00 Day01]# sh change.sh LB00 10.0.0.4
[root@LB00 Day01]# hostname
LB00
[root@LB00 Day01]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
TYPE=Ethernet
BOOTPROTO=none
NAME=eth0
DEVICE=eth0
ONBOOT=yes
IPADDR=10.0.0.4
PREFIX=24
GATEWAY=10.0.0.2
DNS1=223.5.5.5
二、赋值传参
[root@LB00 Day01]# cat change.sh
#!/bin/bash
hostname=$1
ip=$2
hostnamectl set-hostname --static $hostname
sed -i 's#IPADDR.*#IPADDR='$ip'#g' /etc/sysconfig/network-scripts/ifcfg-eth0
systemctl restart network
三、read交互式传参
[root@LB00 Day01]# cat change.sh
#!/bin/bash
read -p "请输入要修改的主机名:" hostname
read -p "请输入要修改的ip:" ip
hostnamectl set-hostname --static $hostname
sed -i 's#IPADDR.*#IPADDR='$ip'#g' /etc/sysconfig/network-scripts/ifcfg-eth0
systemctl restart network
[root@LB00 Day01]# sh change.sh
请输入要修改的主机名:LB0
请输入要修改的ip:10.0.0.3
#因为修改了ip,Xshell断开了
我是koten,10年运维经验,持续分享运维干货,感谢大家的阅读和关注!