1.bash
什么是 Shell
- Shell 是命令解释器,用于解释用户对操作系统的操作
- Shell 有很多
- cat /etc/shells
- CentOS 7 默认使用的 Shell 是 bash
Linux 的启动过程
BIOS -> MBR -> BootLoader(grub) -> kernel -> init -> 系统初始化 -> shell
# 读取磁盘446 的信息,都是包含 mbr.bin的信息
dd if=/dev/sda of=mbr.bin bs=446 count=1
# 通过hexdemp单字节字符显示
hexdump -C mbr.bin
# grub存放路径
/boot/grub2
# 加载内核 kernel
grub2-editenv list # 查看加载的版本信息
# centOS6 启动方式
# init方法
which init
# /usr/sbin/init
# 执行init程序的文件路径
cd /etc/rc.d
# centOS7 启动方式
# 系统sytemd启动文件
cd /usr/lib/systemd/system
# 通过 grub2 配置
file /sbin/grub2-mkconfig
Shell 脚本
- UNIX 的哲学:一条命令只做一件事
- 为了组合命令和多次执行,使用脚本文件来保存需要执行的命令
- 赋予该文件执行权限(chmod u+rx filename)
# 单行 ;分号分隔 实现多命令执行
cd /var/ ; ls
cd /var/ ; ls ; pwd ; du -sh *
# 多行 通过sh文件里面 换行实现
vi test.sh
cd /var/
ls
pwd
du -sh *
# 赋予权限
chmod u+x test.sh
test.sh # 执行
# 通过 命令行 bash 执行
bash test.sh # 执行
# 或者文件头里面指定
# "#"号开头的注释
# Sha-Bang
#!/bin/bash
2.执行命令
内建命令
- 内建命令不需要创建子进程
- 内建命令对当前 Shell 生效
# 即可以修改当前bash的环境变量
source ./filename.sh
. filename.sh
外部命令的
另外启动一个线程执行,有自己独立的变量环境,不影响当前登录执行bash环境
bash ./filename.sh
./filename.sh
子进程与子 Shell
- 子进程是 Shell 程序,称作子 Shell
- 内部命令的结果不会传递给子 Shell
3.管道与重定向
管道与管道符
- 管道和信号一样,也是进程通信的方式之一
- 匿名管道(管道符)是 Shell 编程经常用到的通信工具
- 管道符是"|",将前一个命令执行的结果传递给后面的命令
ps | cat
echo 123 | ps
cat anaconda-ks.cfg | more
cat | ps -ef
重定向符号
一个进程默认会打开标准输入、标准输出、错误输出三个文件描述符
# 输入重定向符号 " <"
echo 'xxxx' > a.txt
read myvar < a.txt # 读取一个变量
echo $myvar # 输出变量
# 输出重定向符号 ">"
echo 123 > /a2.txt # 原有的覆盖
# ">>" 追加
echo 'xxxx' > a.txt
echo '2' > a.txt
cat a.txt # 追加显示 xxxx2
# 2> 如果有错误,输出错误到文本
# &> 先清除 ,如果有错误,输出错误到文本
# EOF 自定义终止符 END Of File
# 输出文本
cat > /path/to/a/file << EOF
lam $USER
EOF
# 当同时创在读取和写入,优先执行读取,最后写入
echo "bbb" > b.txt
echo 'aaa' > c.txt < b.txt
cat c.txt # 结果为 aaa
4.变量
变量的定义
- 变量名的命名规则
- 字母、数字、下划线
- 不以数字开头
变量的赋值
- 为变量赋值的过程,称为变量替换
- 变量名=变量值
- 变量值有空格等特殊字符可以包含在""或'' 中
cmd='ls' # ''代表字符串
echo $cmd # 输出ls文字
cmd='ls' # ''代表字符串
$cmd # 输出文件夹列表
cmd=`ls` # ``代表执行的命令
echo $cmd # 输出文件夹列表
cmd=(ls) # ()代表执行的命令
$cmd # 直接执行 不加echo,输出文件夹列表
### 变量的引用
- ${变量名}称作对变量的引用
- echo ${变量名}查看变量的值
- ${变量名}在部分情况下可以省略为 $变量名
str=123 # 注意等号左边不能有空格
echo $str00 # 错误
echo ${str}00 # 12300
## 变量的作用范围
export # 变量的导出 全局 $PATH 就是提前导出的全局变量
unset # 变量在全局 删除
# 测试子线程变量作用域
myvar='hi jason' # 定义全局变量
vi test.sh
# test.sh内容
echo ${myvar}
chmod u+x test.sh #赋予执行权限
test.sh # 子进程执行 输出为空
bash test.sh # 子进程执行 输出为空
source ./test.sh # 当前进程执行 输出hi jason
. test.sh # 当前进程执行 输出hi jason
export myvar # 导出当前变量到系统全局
test.sh # 子进程执行 可以访问全局 输出hi jason
unset myvar # 删除全局
test.sh # 再次子进程执行 全局已经不存在 输出为空
## 系统环境变量
# 在全局添加自己的运行命令
cd /root
echo 'hi jason' > mysh.sh
chmod u+x mysh.sh
PATH=$PATH:/root
echo $PATH #检查是否生效
mysh.sh # 在任何一个文件夹都可以自由使用mysh.sh命令
# set
set # 查看所有已经设置的变量
# env
env # 查看所有env信息
# $?
ifconfig xxx # 查询失败
echo $? # 查询失败返回1,查询正常返回0
echo $! #
echo $$ # 当前执行的id
echo $0 # 用于执行的命令的工具
# 参数例子
vi test.sh
#!/bin/bash
para1=$1
para2=$2
echo ${para1}
echo ${para2}
./test.sh -a -b
# 输出
-a
-b
# $PS1 设置命令行工具 前面显示的内容(用于区分当前操作的服务器)
echo $PS1 #默认 '\u@\h \W\$ '
PS1 = '\u@\h \W\$ ' # 自定义
PS1="\[\033[0;33m\]\u@$ip \[\033[0;34m\] \w $?:\[\033[00m\] " # 包含ip
环境变量配置文件
# 配置文件 加载顺序
# .d 指定的文件夹
/etc/profile
/etc/profile.d/ # 文件夹
~/.bash_profile
~/.bashrc
/etc/bashrc
# bashrc 全称:bash resource configure
# 当导出全局变量
export PATH=$PATH:/root/mypath
# 触发
~/.bashrc
/etc/bashrc
bash # 执行bash 触发 ~/.bashrc
source /etc/bashrc 触发 /etc/bashrc
其他
# 数组
IPTS=(10.0.0.1 10.0.0.2 10.0.0.3)
# 显示数组的所有元素
echo ${IPTS[@]}
# 显示数组元素个数
echo ${#IPTS[@]}
# 显示数组的第一个元素
echo ${IPTS[0]}
5.运算符
# 赋值运算符
= 赋值运算符,用于算数赋值和字符串赋值
使用 unset 取消为变量的赋值
= 除了作为赋值运算符还可以作为测试操作符
# 算数运算符
# 基本运算符
+ - * / ** %
#使用 expr 进行运算,只支持整数
expr 1 + 2 # 加号两边必须有空格
num=`expr 1 + 2 `
echo $num
# let 数字常量
let "变量名 = 变量值"
#变量值使用 0 开头为八进制
#变量值使用 0x 开头为十六进制
let a=5+4
echo $a
# (()) 双圆括号 let 命令的简化
(( a = 10 ))
(( a++ ))
echo $((10+20))
(( b=1+2 ))
echo $b
转义与引用
# 特殊字符:一个字符不仅有字面意义,还有元意(meta-meaning)
# 注释
; 分号
\ 转义符号
"和' 引号
a=100
echo "$a" # 输出100
echo "/$a" # 输出 $a
echo '$a' # 输出$a
# 转义符号
单个字符前的转义符号
\n \r \t 单个字母的转义
\$ \" \\ 单个非字母的转义
# 引用
常用的引用符号
" 双引号
' 单引号
` 反引号
特殊符号大全
# 引号
' 单引号 完全引用
" 双引号 不完全引用
` 反引号 执行命令·
# 括号 ( ) (( )) $( ) 小括号
单独使用会产生新的子 shell
# 方括号
# [ ] 单独使用方括 比较只能用 -gt 等 不能用 < >比较
测试(test)或数组元素功能
[ 5 -gt 7 ] # 5 > 7
echo $? # 返回false 1
# [ [] ] 两个方括号 支持 > < 直接比较大小
[[ 5 > 7 ]] # 5 > 7
echo $? # 返回false 1
# <> 重定向
# 花括号 { }
- 输出范围
echo {1..9}
- 文件复制
touch a.txt
cp -v ./a.txt{,.bak1} # -v是显示复制的情况
# 运算符号和逻辑符号
+ - * / %
> < =
&& || !
# 转义符号
\n
\'
# 其他符号
空指令 :
~
6.测试与判断
# 退出与退出状态
# exit
exit 10 返回10给 Shell,返回值非 0 位不正常退出
cat > test.sh <<EOF
pwd
exit 127
EOF
chmod u+x test.sh
test.sh # 这里执行 只是子进程退出
echo $? # 打印显示127
# $? 判断当前 Shell 下前一个进程是否正常退出
# 测试命令 test
test 命令利用程序 正常 0 或 失败 1
# 常用文件测试操作符
-d 测试是否目录
-e 测试目录或文件是否存在
-f 测试是否文件
-r 测试是否有读权限
-w 测试是否有写权限
-x 测试是否有执行权限
-L 测试是否为符号连接文件
# 文件测试
test -f /etc/passwd # 判断passwd文件是否存在,
echo $? # 输出0 存在
[ -d /etc/] # 判断 /etc/文件夹是否存在
echo $? # 输出0 存在
# 常用的整数值比较操作符
-eq: 等于 # equal
-ne: 不等于 # not equal
-gt:大于 # greater than
-lt:小于 # less than
-le:小于或等于 # less then or equal
-ge:大于或等于 # greater then or equal
# 整数比较测试
[ 5 -gt 7 ] # 判断5 > 7
echo $? # 输出1 不存在
# 字符串比较操作符
字符串比较格式
[字符串1 = 字符串2] # =:字符串内容相同
[字符串1 != 字符串2] # !=:字符串内容不同
[-z 字符串] # -z:字符串内容为空
# 字符串测试
[ 'jason' = 'jason' ]
echo $? # 输出0 存在
# 使用 if-then语句
test 测试语句可以简化为 [ ] 符号
# if-then 语句的基本用法
# 换行写法
if [ 测试条件成⽴ ] # 当条件为 0 才执行
then 执⾏相应命令
fi 结束
if [ $USER = 'root' ]
then echo "当前用户是管理员${USER}"
fi
# 单行 ;分号写法
if [ $USER = 'root' ]; then echo "当前用户是管理员${USER}" ; fi
# 使用 if-then-else 语句
if-then-else 语句可以在条件不成立时也运行相应的命令
if [ 测试条件成⽴ ]
then 执⾏相应命令
else 测试条件不成⽴,执⾏相应命令
fi 结束
# 嵌套 if 的使用
if 条件测试中可以再嵌套 if 条件测试
嵌套的结果和复合比较语句 && 结果相同
7.分支
case 语句和select语句可以构成分支
case "$变量" in
"情1")
命令...;;
"情况2")
命令...;;
* ) # * 为上面都不满足时执行
命令... ;;
esac
# 实例
vi test
case "$1" in
"start" | "START" )
echo '111';;
"stop" )
echo '222';;
* )
echo '333';;
esac
./test # 输出 333
./test start # 输出 111
./test stop # 输出 222
./test xxx # 输出 333
8.循环
# 使用 for 循环遍历命令的执行结果
# for 循环的语法
for 参数 in 列表
do 执⾏的命令
done 封闭⼀个循环
# 使用反引号或 $() 方式执行命令,命令的结果当作列表进行处理
# 使用 for 循环遍历变量和文本
1. 列表中包含多个变量,变量用空格分隔
2. 对文本处理,要使用文本查看命令取出文本内容
3. 默认逐行处理,如果文本出现空格会当做多行处理
for i in {1..9};
do echo 'hi';
echo $i;
done
# 批量重命名后缀名 jpg -> png
touch {a..d}.jpg
for name in `ls *.jpg`;
do
mv $name $(basename $name .jpg ).png;
done
# C 语言风格的 for 命令
for ((i =1; i <= 10; i++ ));
do
echo $i
done
# while 循环
a=0
while [ $a -lt 13 ];
do
echo "${a}";
((a++));
done
# 死循环
while test测试⼀直成⽴
do
命令
done
# 例子
while :
do
echo '111'
done
# until 循环
until 循环与 while 循环相反,循环测试为假时,执行循环,为真时循环停止
# 例子
var=0
until [ $var -eq 90 ]; # 刚开始 0 = 90 不成立 所以符合条件
do
echo $var;
var=$(($var + 1)); # 当var = 90 时条件为ture 不符合条件退出
done
# 配置循环
for name in /etc/profile.d/*.sh;
do
echo $name ;
done
# 循环的使用
1. 循环和循环可以嵌套
2. 循环中可以嵌套判断,反过来也可以实现嵌套
3. 循环可以使用 break或 shift 和 continue 语句在循环中退出
4.
# 使用循环处理命令行参数
命令行参数可以使用 $1 $2 … ${10}… $n 进行读取
$0 代表脚本名称
$* 和 $@ 代表所有位置参数
$# 代表位置参数的数量
使用 $1_ 方式代替 $1 避免变量为空导致的遗产
# 例子1
vi test3
for para in $*
do
if [ "$para" = "p1" ]
then
echo '命中参数p1'
fi
done
./test3 # 无提示
./test3 p1 # 提示命中参数p1
# 例子2
vi test4
while [ $# -gt 3 ]
do
echo '111'
break;
done
./test4 p1 p2 p3 p4 # 提示一次 111
9.函数
# 自定义函数
函数用于"包含"重复使用的命令集合
# 例子
function myfun(){ # 定义函数
cd /tmp
mkdir {a..d}
ls
}
myfun # 执行
# 定义函数局部变量
local 参数
# 函数的参数
$1 $2 $3 … $n
# 例子
function testPID(){
local myvar
for para in $*
do
if [ -d "/proc/${para}" ]
then
echo '包含改进程pid'
fi
done
}
testPID 1 # 提示包含
testPID 10021 # 无提示
# 系统脚本
# 系统自建了函数库,可以在脚本中引用
cat /etc/init.d/functions
# 自建函数库
使用 source 函数脚本文件"导入"函数
source /etc/init.d/functions
checkpid 1 # 查看pid是否存在
$? # 输出0 存在
10.脚本优先级控制
- 可以使用 nice 和 renice 调整脚本优先级
- 系统会根据脚本内容调整优先级
- 死循环
# 控制执行资源
ulimit -a # 查看系统现在
func() { func func& } # 写一个死循环
func # 调用
:(){ :|:& } ; : # 简写 :永远都为真
func() { func | func& };func # 等价于上面
11.捕获信号
捕获信号脚本的编写
- kill 默认会发送15号信号给应用程序
- ctrl+c 发送2号信号给应用程序
- 9号信号不可阻塞
例子
vi test1
trap "echo sig 15" 15
trap "echo sig 2" 2
echo $$
while :
do
:
done
# 终端1 输入
./test1 # 启动,输入PID 54535
# 终端2 kill
kill -15 54535 # 终端1 输出 sig 15
kill -2 54535 # 终端1 输出 sig 2
kill -9999 54535 # 终端1 进程结束
12.计划任务
一次性计划任务
- 计划任务: 让计算机在指定的时间运行程序
- 计划任务分为: 一次性计划任务 周期性计划任务
- 一次性计划任务
- at
# 修改启动级别
chkconfig --level 35 atd on
# 启动atd
service atd start
# 写入计划
echo "date > ~/qxl/a.txt" | at now +3min
# 或者
at 10:30 # 当前时间+几分钟
echo 'hello' > /tmp/hello.txt
<EOT> # ctrl +d 结束
# 查看
atq
# 查看当前系统时间
date
crond周期性计划任务
crond是Linux系统中的一个守护进程
crond由两个主要的配置文件组成:
/etc/crontab:系统级别的crontab文件,其中包含了定期执行的系统级别任务。/var/spool/cron:用户级别的crontab文件。每个用户都可以在该目录下创建自己的crontab文件,用于执行自己的定期任务。
所有用户定义的 crontab 文件都被保存在 /var/spool/cron目录中
命令
/sbin/service crond start #启动服务
/sbin/service crond stop #关闭服务
/sbin/service crond restart #重启服务
/sbin/service crond reload #重新载入配置
crond # 直接运行
# 检测 crond
service crond status
# 检测 开机启动状态 图形界面
yum intall ntsysv
ntsysv
# 加入开机自动启动:
chkconfig –-level 35 crond on
# 重启服务
service crond restart #重启服务
例子
vi /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute 分钟 (0 - 59)
# | .------------- hour 小时 (0 - 23)
# | | .---------- day of month 一个月中的第几天 (1 - 31)
# | | | .------- month 月份 (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7)星期中星期几 (0 - 6) (星期天 为0)
# | | | | |
# * * * * * user-name command to be executed
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
# 配置 默认编辑的是当前用户
crontab -e
# 编辑
* * * * * date >> /tmp/date.txt # 全部 * 为最小执行单位 每分钟执行一次
# 查看日志
tail -f 100 /var/log/cron
# 最终生成的记录地址 如果是root用户编辑 则保存为root
cat /var/spool/cron/root
anacontab
- 如果计算机不能按照预期时间运行,会在下次开机重新执行
# anacontab 延时计划任务
# 主配置文件
vi /etc/anacrontab
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root
RANDOM_DELAY=45
#最大随机廷迟
#the jobs will be started during the following hours only
START_H0URS_RANGE=3-22
#fanacron的执行时间范围是3:00~22:00
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
#每天开机 5 分钟后就检查 /etc/cron.daily 目录内的文件是否被执行,如果今天没有被执行,那就执行
7 25 cron.weekly nice run-parts /etc/cron.weekly
#每隔 7 天开机后 25 分钟检查 /etc/cron.weekly 目录内的文件是否被执行,如果一周内没有被执行,就会执行
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
#每隔一个月开机后 45 分钟检查 /etc/cron.monthly 目录内的文件是否被执行,如果一个月内没有被执行,那就执行
vi /etc/cron.d/0hourly
# /etc/cron.daily/ 每天会延迟执行的命令
vi /etc/cron.daily/logrotate
13.flock 锁文件
#flock
-x # 加上锁
-s # 获取锁
# 终端1
touch flock.lock
flock -x flock.lock -c 'sleep 30'
# 终端2
flock -s flock.lock -c 'echo hello' # 等待30秒后会输出 hello
14.常用命令
# 修改日期
sudo date -s "2023-05-09 11:30:00"
# 还原系统时间
yum install ntpdate
ntpdate ntp.ubuntu.com