Shell信號發送與捕捉 什麼是跳板機

信號(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     這種命令是不能執行的
 
echo 'test' | rm -f   這種也是沒有效果的
 
 

這兩個命令只接受命令行參數中指定的處理內容,不從標準輸入中獲取處理內容。

但是有時候我們的腳本卻需要 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:" Action

       case $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服務連接到跳板機:

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章