信号(Signal):信号是在软件层次上对中断机制的一种模拟,通过给一个进程发送信号,执行相应的处理函数。
linux通过信号来在运行在系统上的进程之间通信,也可以通过信号来控制shell脚本的运行
进程可以通过三种方式来响应一个信号:
1)忽略信号,即对信号不做任何处理,其中有两个信号不能忽略:SIGKILL及SIGSTOP。
2)捕捉信号。
3)执行缺省操作,Linux对每种信号都规定了默认操作。
Linux支持两种信号:
一种是标准信号,编号1-31,称为非可靠信号(非实时),不支持队列,信号可能会丢失,比如发送多次相同的信号,进程只能收到一次,如果第一个信号没有处理完,第二个信号将会丢弃。
另一种是扩展信号,编号32-64,称为可靠信号(实时),支持队列,发多少次进程就可以收到多少次。
编号 |
信号名称 |
缺省动作 |
描述 |
1 |
SIGHUP |
终止 |
终止进程,挂起 |
2 |
SIGINT |
终止 |
键盘输入中断命令,一般是CTRL+C |
3 |
SIGQUIT |
CoreDump |
键盘输入退出命令,一般是CTRL+\ |
4 |
SIGILL |
CoreDump |
非法指令 |
5 |
SIGTRAP |
CoreDump |
trap指令发出,一般调试用 |
6 |
SIGABRT |
CoreDump |
abort(3)发出的终止信号 |
7 |
SIGBUS |
CoreDump |
非法地址 |
8 |
SIGFPE |
CoreDump |
浮点数异常 |
9 |
SIGKILL |
终止 |
立即停止进程,不能捕获,不能忽略 |
10 |
SIGUSR1 |
终止 |
用户自定义信号1,像Nginx就支持USR1信号,用于重载配置,重新打开日志 |
11 |
SIGSEGV |
CoreDump |
无效内存引用 |
12 |
SIGUSR2 |
终止 |
用户自定义信号2 |
13 |
SIGPIPE |
终止 |
管道不能访问 |
14 |
SIGALRM |
终止 |
时钟信号,alrm(2)发出的终止信号 |
15 |
SIGTERM |
终止 |
终止信号,进程会先关闭正在运行的任务或打开的文件再终止,有时间进程在有运行的任务而忽略此信号。不能捕捉 |
16 |
SIGSTKFLT |
终止 |
处理器栈错误 |
17 |
SIGCHLD |
可忽略 |
子进程结束时,父进程收到的信号 |
18 |
SIGCONT |
可忽略 |
让终止的进程继续执行 |
19 |
SIGSTOP |
停止 |
停止进程,不能忽略,不能捕获 |
20 |
SIGSTP |
停止 |
停止进程,一般是CTRL+Z |
21 |
SIGTTIN |
停止 |
后台进程从终端读数据 |
22 |
SIGTTOU |
停止 |
后台进程从终端写数据 |
23 |
SIGURG |
可忽略 |
紧急数组是否到达socket |
24 |
SIGXCPU |
CoreDump |
超出CPU占用资源限制 |
25 |
SIGXFSZ |
CoreDump |
超出文件大小资源限制 |
26 |
SIGVTALRM |
终止 |
虚拟时钟信号,类似于SIGALRM,但计算的是进程占用的时间 |
27 |
SIGPROF |
终止 |
类似与SIGALRM,但计算的是进程占用CPU的时间 |
28 |
SIGWINCH |
可忽略 |
窗口大小改变发出的信号 |
29 |
SIGIO |
终止 |
文件描述符准备就绪,可以输入/输出操作了 |
30 |
SIGPWR |
终止 |
电源失败 |
31 |
SIGSYS |
CoreDump |
非法系统调用 |
stty(set tty,设置tty)命令用于检查和修改当前注册的终端的通信参数。
UNIX系统为键盘的输入和终端的输出提供了重要的控制手段,可以通过stty命令对特定终端或通信线路设置选项。
可以使用stty -a命令查看当前注册终端的设置情况。
捕捉信号
trap命令定义shell脚本在运行时根据接收的信号做相应的处理。
命令格式:trap: usage: trap [-lp] [[arg] signal_spec ...]
-l #打印编号1-64编号信号名称
arg # 捕获信号后执行的命令或者函数
signal_spec # 信号名或编号
示例:
#!/bin/bash
trap "echo ByeBye~" EXIT
count=1
while [ $count -le 5 ]
do
echo "Loop #$count"
sleep 1
count=$[ $count + 1 ]
done
执行脚本的结果:
trap "" 2 ##信号屏蔽
trap : 2 (trap - 2) ##恢复信号
一般捕捉信号后,做以下几个动作:
1)清除临时文件
2)忽略该信号
3)询问用户是否终止脚本执行
示例1:按CTRL+C不退出循环
#!/bin/bash
trap "" 2 ##屏蔽信号2
count=1
while [ $count -le 5 ]
do
echo "Loop #$count"
sleep 1
count=$[ $count + 1 ]
done
运行脚本的结果:
示例2:循环打印数字,按CTRL+C退出,并打印退出提示
#!/bin/bash
trap "echo 'exit...';exit" 2
count=1
while [ $count -le 5 ]
do
echo "Loop #$count"
sleep 1
count=$[ $count + 1 ]
done
运行脚本的结果:
示例3:让用户选择是否终止循环
#!/bin/bash
trap "fun" 2
function fun(){
read -p "Terminate the process?(Y/N): "char
if [ $char == "Y" ];then
exit
fi
}
for i in `seq 5`
do
echo $i
sleep 1
done
运行脚本的结果:
xargs命令
什么是xargs
xargs命令可以通过管道接受字符串,并将接收到的字符串通过空格分割成许多参数(默认情况下是通过空格分割) 然后将参数传递给其后面的命令,作为后面命令的命令行参数
为什么要用xargs呢,我们知道,linux命令可以从两个地方读取要处理的内容,一个是通过命令行参数,一个是标准输入。
另外很多程序是不处理标准输入的,例如 kill , rm 这些程序如果命令行参数中没有指定要处理的内容则不会默认从标准输入中读取。即:
这两个命令只接受命令行参数中指定的处理内容,不从标准输入中获取处理内容。
但是有时候我们的脚本却需要 echo '516' | kill 这样的效果,例如 ps -ef | grep 'ddd' | kill 这样的效果,筛选出符合某条件的进程pid然后结束。这种需求对于我们来说是理所当然而且是很常见的,那么应该怎样达到这样的效果呢。有几个解决办法:
<1> kill `ps -ef | grep 'ddd'` 这种形式实际上等同于拼接字符串得到的命令,其效果类似于 kill $pid
<2> ps -ef | grep 'ddd' | xargs kill
与管道有什么不同
示例:
[root@server day03]# echo '--help' | cat
输出: --help
该命令输出的是echo的内容,也就是说将echo的内容当作cat处理的文件内容了,实际上就是echo命令的输出通过管道定向到cat的输入了。然后cat从其标准输入中读取待处理的文本内容。
[root@server day03]# echo '--help' | xargs cat
输出:相当于 cat --help的输出,该命令中xargs将其接受的字符串 --help 做成cat的一个命令参数来运行cat命令,同样 echo 'test.c test.cpp' | xargs cat 等价于 cat test.c test.cpp 此时会将test.c和test.cpp的内容都显示出来。
练习:
<1>在/tmp目录下建立形如westos_2019-01-02-10-52-57的文件,其中westos_的后缀是实时的时间,并使用ctrl+C删除这些文件。
#!/bin/bash
trap "find /tmp -type f -name "westos_*" | xargs rm -f && exit " 2
while true
do
touch /tmp/westos_$(date +%F-%H-%M-%S)
sleep 2
ls -l /tmp/westos*
done
执行脚本的结果如下:
<2>在/var/log/secure查找连接失败的主机,并统计次数,累计连接失败超过3次,将此主机加入系统黑名单(/etc/hosts.deny)
#!/bin/bash
cat /var/log/secure | awk '/Failed/{print $(NF-3)}' | sort | uniq -c | awk '{print $2"="$1}' > /tmp/blacklist
MAXCOUNT="3"
for i in `cat /tmp/blacklist`
do
IP=`echo $i | awk -F= '{print $1}'` ##取出主机IP
NUM=`echo $i | awk -F= '{print $2}'` ##取出失败次数
if [ $NUM -ge $MAXCOUNT ];then
##屏蔽IP前前确认此IP是否存在
grep $IP /etc/hosts.deny > /dev/null
if [ $? -gt 0 ];then
echo "sshd:$IP" >> /etc/hosts.deny
fi
fi
done
脚本执行结果:
此时再在主机172.25.254.81和主机172.25.254.200上尝试ssh到测试主机(172.25.254.100)会报错
练习:
(1)使用循环在mariadb数据库中分别建立tom harry natasha三个数据库
(2)将所有库备份成 ‘库名称_年-月-日.sql.gz‘ 的形式
(3)在每个数据库中建立一个表,并向表中插入一组数据
#!/bin/bash
Myuser=root
Mypass=zzz
Mycmd="mysql -u$Myuser -p$Mypass"
Mydump="mysqldump -u $Myuser -p$Mypass"
DBpath=/home/backup
#######建立数据库##########
for dbname in tom harry natasha
do
$Mycmd -e "create database $dbname;"
done
######在数据库中建立表,并插入数据######
for dbname in tom harry natasha
do
$Mycmd -e "use $dbname;create table ${dbname}test(id int,username varchar(20),passwd varchar(20));insert into ${dbname}test values (1,'westos','123')"
done
######备份所有数据库########
[ ! -d $DBpath ] && mkdir -p $DBpath
for dbname in `mysql -uroot -pzzz -e "show databases;" | sed '1,2d' | egrep -v "mysql|schema"`
do
$Mydump $dbname | gzip > $DBpath/${dbname}_$(date +%F).sql.gz
done
######显示每个数据库中所插入表格的内容######
for dbname in tom harry natasha
do
echo =======${dbname}.${dbname}test========
$Mycmd -e "use $dbname;select * from ${dbname}test;"
done
脚本运行结果如下:
练习:
数据库备份,执行scriptname.sh $dbpasswd 备份数据库中所有库到/mnt/mysqldump目录中,备份文件名称为“库名称.sql”,当此文件存在时进入交互模式,询问动作,输入“s”跳过备份,输入“b”,备份名称为“库名称_backup.sql”,输入“o”时,覆盖原文件,输入“e”退出
#!/bin/bash
mkdir -p /mnt/mysqldump
DATABASE=`mysql -uroot -pzzz -e "show databases;" | sed '1,2d' | egrep -v "mysql|schema"`
for dbname in $DATABASE
do
if [ -e /mnt/mysqldump/$dbname.sql ];then
read -p "$dbname has been dumped!
[S]kip [B]ackup [O]verwrite [E]xit
please input the action:" Actioncase $Action in
s|S)
;;
b|B)
mysqldump -uroot -p$1 $dbname > /mnt/mysqldump/${dbname}_backup.sql
;;
o|O)
mysqldump -uroot -p$1 $dbname > /mnt/mysqldump/${dbname}.sql
;;
e|E)
echo -e "Bye~"
continue
esac
else
mysqldump -uroot -p$1 $dbname > /mnt/mysqldump/${dbname}.sql
echo -e "$dbname is backuped!"
fi
done
执行脚本的结果如下:
练习:shell实现跳板机
跳板机就是一台服务器,开发或运维人员在维护过程中首先要统一登录到这台服务器,然后再登录到目标设备进行维护和操作。
很多大公司的服务器都不允许直接登录,而是通过一个跳板机才能登录过去。在跳板机中,通常只能执行几个少数命令(如SSH),而其他命令是不允许执行的,这个需求实质上就是拦截用户的输入,在用户与shell之间加一道门,而脚本的功能,就是根据用户不同的输入采取不同的动作,脚本的长短也就跟需要的命令的多少有关,需要的命令越多,脚本需要判断的就越多,脚本也就越长,但实际并不需要为每个命令都写一个action,只要为每个种类的写一个action就行了,
如果只实现ssh命令,那就是跳板机了。
实验环境:
1.ip:
跳板机IP:172.25.254.100
Server1IP:172.25.254.200
Server2IP:172.25.254.81
2.用户:使用所有主机上都存在的非超级用户student
3.跳板机配置:
<1>配置免密码登录
(1)在跳板机上生成公钥和私钥
(2)将公钥发到server1和server2服务器
[student@server ~]$ ssh-copy-id -i ~/.ssh/id_dsa.pub 172.25.254.81 ####将公钥发送到172.25.254.81的student用户,不指明用户时默认按当前shell的用户
(3)测试:可以在跳板机上免密登陆其他两台服务器
<2>配置跳板机脚本1
#!/bin/bash
function trapper() {
trap "" INT EXIT TSTP TERM HUP
}
function main() {
while true
do
trapper
clear
cat <<menu
1)host1 172.25.254.200
2)host2 172.25.254.81
3)exit
menu
read -p "Please input a number:" num
case $num in
1)
echo "login in 172.25.254.200..."
ssh 172.25.254.200 ##不加用户的话默认按照当前shell的用户登陆服务器
;;
2)
echo "login in 172.25.254.81..."
ssh 172.25.254.81
;;
3)
exit
;;
esac
done
}main
<3>配置跳板机脚本2
新建一个脚本放入开机运行目录下,去调用jiaobanji脚本,处了root用户以外,其他用户开机就执行跳板机脚本
[root@server day04]# cd /etc/profile.d/
[root@server profile.d]# vim user_choice.sh#!/bin/bash
[ $UID -ne 0 ] && sh /mnt/tiaobanji.sh
<4>测试
本机切换到非root用户:
其他测试主机通过ssh服务连接到跳板机:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2021-11-29 不要在linux上启用net.ipv4.tcp_tw_recycle参数
2021-11-29 linux的TCP连接数量最大不能超过65535个吗,那服务器是如何应对百万千万的并发的?
2018-11-29 C/C++程序CPU问题分析
2018-11-29 高CPU排查方法分享
2017-11-29 Wireshark抓包常见问题解析
2015-11-29 Linux netstat命令详解
2013-11-29 《白手起家Win32SDK应用程序》(完整版+目录)