Shell概述
文章目錄
- Shell
- Shell 腳本是什么?
- 語法級
- 可以在 Shell 腳本中使用哪些類型的變量?
- Shell 腳本中 `if` 語法如何嵌套?
- Shell 腳本中 `case` 語句的語法?
- Shell 腳本中 `for` 循環語法?
- Shell 腳本中 `while` 循環語法?
- 如何使腳本可執行?
- 在 Shell 腳本如何定義函數呢?
- 編程題
- 判斷一文件是不是字符設備文件,如果是將其拷貝到 `/dev` 目錄下?
- 添加一個新組為 class1 ,然后添加屬于這個組的 30 個用戶,用戶名的形式為 stdxx ,其中 xx 從 01 到 30 ?
- 寫一個 sed 命令,修改 `/tmp/input.txt` 文件的內容?
- 一、工作中寫過的腳本
- 二、如何獲取一個文件每一行的第三個元素?
- 三、 shell 函數能解決什么實際問題?
- 四、使用 awk 統計 httpd 訪問日志中每個客戶端 IP 的出現次數?
- 五、正則表達式符號: *、+、?、[]、[^]、\{n\}分別代表什么含義?
- 六、shell 中對變量字串進行截取的方式有哪些?
- 七、編寫腳本,用戶輸入密碼,腳本判斷密碼是否正確,輸入正確則提示正確,連續輸入錯誤密碼 3 次,則報警?
- 八、編寫腳本,自動生成一個 8 位隨機密碼?
- 九、遞歸函數,遍歷目錄
- 十、shell實現nginx日志自動切割
- 十一、冒泡算法
- 十二、特殊符號
Shell
Shell 腳本是什么?
一個 Shell 腳本是一個文本文件,包含一個或多個命令。作為系統管理員,我們經常需要使用多個命令來完成一項任務,我們可以添加這些所有命令在一個文本文件(Shell 腳本)來完成這些日常工作任務。
什么是默認登錄 Shell ?
在 Linux 操作系統,"/bin/bash" 是默認登錄 Shell,是在創建用戶時分配的。
使用 chsh 命令可以改變默認的 Shell 。示例如下所示:
## chsh <用戶名> -s <新shell> ## chsh ThinkWon -s /bin/sh在 Shell 腳本中,如何寫入注釋?
注釋可以用來描述一個腳本可以做什么和它是如何工作的。每一行注釋以 # 開頭。例子如下:
#!/bin/bash ## This is a command echo “I am logged in as $USER”語法級
可以在 Shell 腳本中使用哪些類型的變量?
在 Shell 腳本,我們可以使用兩種類型的變量:
-
系統定義變量
系統變量是由系統系統自己創建的。這些變量通常由大寫字母組成,可以通過 set 命令查看。
-
用戶定義變量
用戶變量由系統用戶來生成和定義,變量的值可以通過命令 "echo $<變量名>" 查看。
Shell腳本中 $? 標記的用途是什么?
在寫一個 Shell 腳本時,如果你想要檢查前一命令是否執行成功,在 if 條件中使用 $? 可以來檢查前一命令的結束狀態。
-
如果結束狀態是 0 ,說明前一個命令執行成功。例如:
root@localhost:~## ls /usr/bin/shar /usr/bin/shar root@localhost:~## echo $? 0 -
如果結束狀態不是0,說明命令執行失敗。例如:
root@localhost:~## ls /usr/bin/share ls: cannot access /usr/bin/share: No such file or directory root@localhost:~## echo $? 2
Bourne Shell(bash) 中有哪些特殊的變量?
下面的表列出了 Bourne Shell 為命令行設置的特殊變量。
內建變量 解釋 $0 命令行中的腳本名字 $1 第一個命令行參數 $2 第二個命令行參數 ….. ……. $9 第九個命令行參數 $## 命令行參數的數量 $* 所有命令行參數,以空格隔開如何取消變量或取消變量賦值?
unset 命令用于取消變量或取消變量賦值。語法如下所示:
## unset <變量名>Shell 腳本中 if 語法如何嵌套?
if [ 條件 ] then 命令1 命令2 ….. else if [ 條件 ] then 命令1 命令2 …. else 命令1 命令2 ….. fi fi在 Shell 腳本中如何比較兩個數字?
在 if-then 中使用測試命令( -gt 等)來比較兩個數字。例如:
#!/bin/bash x=10 y=20 if [ $x -gt $y ] then echo “x is greater than y” else echo “y is greater than x” fiShell 腳本中 case 語句的語法?
基礎語法如下:
case 變量 in 值1) 命令1 命令2 ….. 最后命令 !! 值2) 命令1 命令2 …… 最后命令 ;; esacShell 腳本中 for 循環語法?
基礎語法如下:
for 變量 in 循環列表 do 命令1 命令2 …. 最后命令 doneShell 腳本中 while 循環語法?
如同 for 循環,while 循環只要條件成立就重復它的命令塊。
不同于 for循環,while 循環會不斷迭代,直到它的條件不為真。
基礎語法:
while [ 條件 ] do 命令… donedo-while 語句的基本格式?
do-while 語句類似于 while 語句,但檢查條件語句之前先執行命令(LCTT 譯注:意即至少執行一次。)。下面是用 do-while 語句的語法:
do { 命令 } while (條件)Shell 腳本中 break 命令的作用?
break 命令一個簡單的用途是退出執行中的循環。我們可以在 while 和 until 循環中使用 break 命令跳出循環。
Shell 腳本中 continue 命令的作用?
continue 命令不同于 break 命令,它只跳出當前循環的迭代,而不是整個循環。continue 命令很多時候是很有用的,例如錯誤發生,但我們依然希望繼續執行大循環的時候。
如何使腳本可執行?
使用 chmod 命令來使腳本可執行。例子如下:chmod a+x myscript.sh 。
#!/bin/bash 的作用?
#!/bin/bash 是 Shell 腳本的第一行,稱為釋伴(shebang)行。
- 這里 # 符號叫做 hash ,而 ! 叫做 bang。
- 它的意思是命令通過 /bin/bash 來執行。
如何調試 Shell腳本?
- 使用 -x' 數(sh -x myscript.sh)可以調試 Shell腳本。
- 另一個種方法是使用 -nv 參數(sh -nv myscript.sh)。
如何將標準輸出和錯誤輸出同時重定向到同一位置?
- 方法一:2>&1 (如## ls /usr/share/doc > out.txt 2>&1 ) 。
- 方法二:&> (如## ls /usr/share/doc &> out.txt ) 。
在 Shell 腳本中,如何測試文件?
test 命令可以用來測試文件?;A用法如下表格:
Test 用法 -d 文件名 如果文件存在并且是目錄,返回true -e 文件名 如果文件存在,返回true -f 文件名 如果文件存在并且是普通文件,返回true -r 文件名 如果文件存在并可讀,返回true -s 文件名 如果文件存在并且不為空,返回true -w 文件名 如果文件存在并可寫,返回true -x 文件名 如果文件存在并可執行,返回true在 Shell 腳本如何定義函數呢?
函數是擁有名字的代碼塊。當我們定義代碼塊,我們就可以在我們的腳本調用函數名字,該塊就會被執行。示例如下所示:
$ diskusage () { df -h ; } 譯注:下面是我給的shell函數語法,原文沒有 [ function ] 函數名 [()] { 命令; [return int;] }如何讓 Shell 就腳本得到來自終端的輸入?
read 命令可以讀取來自終端(使用鍵盤)的數據。read 命令得到用戶的輸入并置于你給出的變量中。例子如下:
## vi /tmp/test.sh #!/bin/bash echo ‘Please enter your name’ read name echo “My Name is $name” ## ./test.sh Please enter your name ThinkWon My Name is ThinkWon如何執行算術運算?
有兩種方法來執行算術運算:
- 1、使用 expr 命令:## expr 5 + 2 。
- 2、用一個美元符號和方括號($[ 表達式 ]):test=$[16 + 4] ; test=$[16 + 4] 。
編程題
判斷一文件是不是字符設備文件,如果是將其拷貝到 /dev 目錄下?
#!/bin/bash read -p "Input file name: " FILENAME if [ -c "$FILENAME" ];thencp $FILENAME /dev fi添加一個新組為 class1 ,然后添加屬于這個組的 30 個用戶,用戶名的形式為 stdxx ,其中 xx 從 01 到 30 ?
#!/bin/bash groupadd class1 for((i=1;i<31;i++)) doif [ $i -le 10 ];thenuseradd -g class1 std0$ielseuseradd -g class1 std$ifi done編寫 Shell 程序,實現自動刪除 50 個賬號的功能,賬號名為stud1 至 stud50 ?
#!/bin/bash for((i=1;i<51;i++)) douserdel -r stud$i done寫一個 sed 命令,修改 /tmp/input.txt 文件的內容?
要求:
- 刪除所有空行。
- 一行中,如果包含 “11111”,則在 “11111” 前面插入 “AAA”,在 “11111” 后面插入 “BBB” 。比如:將內容為 0000111112222 的一行改為 0000AAA11111BBB2222 。
一、工作中寫過的腳本
1、監控腳本(監控系統、監控服務、監控硬件信息、監控性能、安全監控等)
2、系統初始化腳本(創建目錄,創建賬戶,安裝軟件包,設置權限,修改內核參數等)
一鍵部署(源碼安裝腳本)
3、備份腳本(自動備份數據庫,備份網站數據,備份日志,備份配置文件等)
4、日志分析腳本(分析日志數據,匯總并統計相關信息,如 PV、UV 等)
二、如何獲取一個文件每一行的第三個元素?
# awk ‘{print $3}’ 文件名
備注:awk 支持按列輸出,通過內置變量$1,$2,$3…可以單獨顯示任意列,默認列是以
空格或 Tab 縮進為分隔符,也可以使用-F 選項指定其他分隔符。
三、 shell 函數能解決什么實際問題?
定義函數的格式:
function 函數名{
代碼塊
}
函數名(){
代碼塊
}
使用函數可以避免代碼重復
使用函數可以將大的工程分割為若干小的功能模塊,代碼的可讀性更強
四、使用 awk 統計 httpd 訪問日志中每個客戶端 IP 的出現次數?
# awk ‘{ip[$1]++}END{for(i in ip){print ip[i],i}}’ /var/log/httpd/access_log
備注:定義數組,數組名稱為 ip,數字的下標為日志文件的第 1 列(也就是客戶端的 I
P 地址)
,++的目的在于對客戶端進行統計計數,客戶端 IP 出現一次計數器就加 1。END
中的指令在讀取完文件后執行,通過循環將所有統計信息輸出。
4、哪些方式可以將標準輸出和錯誤輸出重定向到文件?
答案:
# 命令 &> 文件名
# 命令 > 文件名 1 2> 文件名 2
# 命令 > 文件名 2>&1
# 命令 &>> 文件名
# 命令 >> 文件名 1 2>> 文件名 2
# 命令 >> 文件名 2>&1
五、正則表達式符號: *、+、?、[]、[^]、{n}分別代表什么含義?
*表示匹配前面的字符出現了任意次(包括 0 次)
+表示匹配前面的字符出現了至少 1 次(1 次或多次)
?表示匹配前面的字符出現了 0 次或 1 次
[]表示集合,匹配集合中的任意單個字符
[^]表示對集合取反
{n}表示精確匹配前面的字符出現了 n 次
六、shell 中對變量字串進行截取的方式有哪些?
# echo ${變量名:開始位置:長度} #注意,起始位置從 0 開始
# expr substr $變量名 #注意,起始位置從 1 開始
# echo $變量名 |
開始位置 長度
cut -b 開始位置-結束位置 #注意,起始位置從 1 開始
\7. 使用 sed 命令如何將文件中所有的大寫字母 Q 轉換為小寫字母 q?
答案:
# sed -i ‘s/Q/q/g’ 文件名
七、編寫腳本,用戶輸入密碼,腳本判斷密碼是否正確,輸入正確則提示正確,連續輸入錯誤密碼 3 次,則報警?
vim test.sh#!/bin/bashinit=123456for I in {1..3}doread -p "請輸入密碼:" passif [ $pass == $init ];thenecho "密碼正確"breakfidoneecho "警告:密碼錯誤"八、編寫腳本,自動生成一個 8 位隨機密碼?
答案:
vim test.sh#!/bin/bashStr="abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"pass=""for i in {1..8}donum=$[RANDOM%${#Str}]tmp=${Str:num:1}pass+=$tmpdoneecho $pass九、遞歸函數,遍歷目錄
遞歸遍歷目錄
通過定義遞歸函數 files來實現
Shell也可以實現遞歸函數,就是可以調用自己本身的函數。在Linux系統上編寫Shel腳本的時候,經常需要遞歸遍歷系統的目錄,列出目錄下的文件和目錄,逐層遞歸列出,并對這些層級關系進行展示。具體的實現過程如下所示。
function list files(){ for f in `ls $1`; do if [-d"$1/Sf"]: then echo"$2$f" list_files "$1/$2" " $2" else echo "$2$" fi done}list_files "/var/log" "十、shell實現nginx日志自動切割
腳本思路【按天分割日志】
a、獲取昨天的日期(date -d yesterday +%Y%m%d),用來作為分割后日志的名稱
b、將源日志文件移動到新的nohuplogs文件夾里,并按時間重命名
c、在源日志文件夾(logs)里新建默認日志文件(access.log)
d、給nginx一個信號量,重新打開日志
f、設置一個定時任務,定時執行日志切割的腳本
操作步驟
a、新建日志分割的文件夾nohuplogs(mkdir /jboss/nginx/nohuplogs)
b、編寫腳本,暫且命名為:splitlogs.sh吧,腳本內容如下:
LOGPATH=/jboss/nginx/logs/access.logBASEPATH=/jboss/nginx/nohuplogsLOGBAK=$BASEPATH/$(date -d yesterday +%Y%m%d).log#echo $LOGBAKmv $LOGPATH $LOGBAKtouch $LOGPATHkill -USR1 `cat /jboss/nginx/logs/nginx.pid`
c、配置定時任務 crontab -e,如下圖,增加日志分割的腳本,每天晚上23點59分切割
59 23 * * * sh /jboss/nginx/splitlogs.sh
十一、冒泡算法
請結合使用冒泡排序方法把 123.txt 文件中的數字按照降序排序輸出在一行當中,并要求沒有重復數字。(20分)
cat 123.txt
1 4 7 9
2 5 8 3
3 6 9 7
#!/bin/bashmyarray() {array=(`echo $@`)\#echo ${array[*]}a=${#array[*]}for ((i=1; i<$a; i++))do for ((j=0; j<$a-i; j++)) do if [ ${array[$j]} -lt ${array[$[$j + 1]]} ];then temp=${array[$j]} array[$j]=${array[$[$j+1]]} array[$[$j+1]]=$temp fi donedoneecho ${array[*]}}###主體代碼
list=`for i in $(cat 123.txt); do echo $i; done | sort -n | uniq`a=0for value in $listdo arr[$a]=$value let a++doneresult=`myarray ${arr[*]}`echo “排序后數組為:$result”
監控網站狀態是否正常,異常發郵件
題目要求:
寫一個shell腳本,通過curl -I 返回狀態碼來判定所訪問的網站是否正常,比如當代碼狀態200,才算正常
寫一個發郵件的腳本
習題分析:
1、關鍵問題,截取出代碼狀態
2、在寫出該shell腳本時,應該先在命令下面使用curl -I http://www.51xit.top/命令測試,然后通過awk截取到狀態碼
3、寫發郵件的腳本,用的是sendEmail。生產環境有配套的模板
4、判斷和發郵件關聯
curl -I http://www.51xit.top/
我們抓包會有交互信息 200
###創建觸發器及郵件報警測試##
【安裝郵件組件】
[root@tang ~]# wget http://caspian.dotconf.net/menu/Software/SendEmail/sendEmail-v1.56.tar.gz
[root@tang ~]# tar -zxvf sendEmail-v1.56.tar.gz
[root@tang ~]# cp sendEmail-v1.56/sendEmail /usr/local/bin/
[root@tang ~]# chmod 755 /usr/local/bin/sendEmail
[root@tang ~]# vi /opt/sendEmail.sh
#!/bin/bash
#
# Filename: SendEmail.sh
# Revision: 1.0
# Date: 2019/05/29
# Author: Qicheng
# Email:
# Website: http://51xit.top/
# Description: tang郵件告警腳本
# Notes: 使用sendEmail
#
# 腳本的日志文件
LOGFILE="/tmp/Email.log"
:>"$LOGFILE"
exec 1>"$LOGFILE"
exec 2>&1
SMTP_server=‘smtp.qq.com’ # SMTP服務器,變量值需要自行修改
username=‘1581273154@qq.com’ # 用戶名,變量值需要自行修改
password=‘cgdxhxqddtrafijh’ # 密碼(QQ郵箱用的是授權碼),變量值需要自行修改
from_email_address=‘1581273154@qq.com’ #### 發件人Email地址,變量值需要自行修改
to_email_address="$1" # 收件人Email地址,tang傳入的第一個參數
message_subject_utf8="$2" # 郵件標題,tang傳入的第二個參數
message_body_utf8="$3" # 郵件內容,tang傳入的第三個參數
# 轉換郵件標題為GB2312,解決郵件標題含有中文,收到郵件顯示亂碼的問題。
message_subject_gb2312=`iconv -t GB2312 -f UTF-8 << EOF
$message_subject_utf8
EOF`
[ KaTeX parse error: Expected 'EOF', got '&' at position 11: ? -eq 0 ] &?& message_subje…message_subject_gb2312" || message_subject="$message_subject_utf8"
# 轉換郵件內容為GB2312,解決收到郵件內容亂碼
message_body_gb2312=`iconv -t GB2312 -f UTF-8 << EOF
$message_body_utf8
EOF`
[ KaTeX parse error: Expected 'EOF', got '&' at position 11: ? -eq 0 ] &?& message_body=…message_body_gb2312" || message_body="$message_body_utf8"
# 發送郵件
sendEmail=’/usr/local/bin/sendEmail’
set -x
sendEmail?s"sendEmail -s "sendEmail?s"SMTP_server" -xu “username"?xp"username" -xp "username"?xp"password” -f “fromemailaddress"?t"from_email_address" -t "frome?maila?ddress"?t"to_email_address” -u “messagesubject"?m"message_subject" -m "messages?ubject"?m"message_body” -o message-content-type=text -o message-charset=gb2312
[root@tang ~]# chmod +x /opt/sendEmail.sh
[root@tang ~]# /opt/sendEmail.sh 1581273154@qq.com 測試 測試
監控磁盤情況,異常發郵件
#!/bin/bashdisk_sda1=df -h |sed -n '3p'|awk '{print $4}'|cut -f 1 -d '%'if ((disk_sda1 > 80));then echo “this is error” echo date “192.168.56.128 this is over 70%” |mail -s “disk over 70%” 12345300@qq.com,5645645@qq.comelse echo “this is ok”fi計劃任務:
[root@localhost tmp]# crontab -e添加
3個小時檢查一次\* */3 * * * /var/tmp/check_disk.sh或者十分鐘檢查一次*/10 * * * * /var/tmp/check_disk.sh十二、特殊符號
$0:當前腳本的文件名
$n:第n個位置參數
?:傳遞給腳本或函數的所有參數,*:傳遞給腳本或函數的所有參數,?:傳遞給腳本或函數的所有參數,*會將這些參數視為一個整體
@:傳遞給腳本或函數的所有參數,@:傳遞給腳本或函數的所有參數,@:傳遞給腳本或函數的所有參數,@會將所有參數當作同一字符串中的多個獨立的單詞
$#:腳本運行時攜帶的參數個數
$*:表示所有位置參數的內容
$?:最近一個命令的退出狀態碼
$$:當前shell的進程ID(PID)
$!:最近一個后臺命令的PID
!!:執行上一條命令
IFS:內部字段分隔符,IFS環境變量定義了shell用作字段分隔符的一系列字符。默認情況下,shell會將下列字符當做字段分隔符:
- 空格
- 制表符
- 換行符
&>:將STDERR和STDOUT的輸出重定向到同一個輸出文件
&-:要關閉文件描述符,可以將它重定向到特殊符號&-
.:點操作符,點操作符是source命令的別名,它會在shell上下文中執行點操作符指定的腳本,而不是創建一個新的shell。
[ 操作符 文件或目錄 ]
常用的測試操作符
-d:測試是否為目錄(Directory)
-e:測試目錄或文件是否存在(Exist)
-f:測試是否為文件(File)
-r:測試當前用戶是否有權限讀取(Read)
-w:測試當前用戶是否有權限寫入(Write)
-x:測試當前用戶是否有權限執行(eXcute)
總結
- 上一篇: 网络大全解答
- 下一篇: Nginx 服务优化与防盗链