《Linux.Shell編程從入門到精通》讀書筆記


第一章 第一個Shell程序

  • 以 #!解析器名稱 開頭,表示選擇哪個解釋器解釋shell腳本
  • source命令
  • export命令
  • env命令
  • unset命令
    第二章 shell編程基礎
  • 函數傳遞
  • 標准輸入輸出符號
    第三章 編程的基本元素
  • 獲取命令執行返回值
  • $((數值計算)) 獲取數值計算結果 如:$((100 - 2*100/400))
  • 替換運算符
  • 模式匹配運算符
  • shift命令可以截取參數列表最左端的一個參數
  • type命令判斷被執行命令的來源(別名、關鍵字、函數、內置命令、外部命令)
  • test命令用於評估表達式,返回零(true)或者非零(false)
  • 邏輯運算符
  • 運算符號
  • 執行反單引號(`)之間的命令,引用結果作為字符串
  • getopt命令用於分析命令標志和參數
    第四章 正則表達式
  • 元字符
  • POSIX字符集
  • 后向引用
  • 交替、分組
    第五章 基本文本處理
  • 排序文本
  • 文本去重
  • 統計文本
  • 打印和格式化輸出
  • 提取文本開頭和結尾
  • 字段處理
  • 文本替換
  • Linux下的配置文件
    第六章 文件和文件系統
  • 文件
  • 文件系統
    第七章 流編輯器sed
  • 工作地址范圍
  • 命令
  • [:特殊字符:]用於匹配特俗字符
    第八章 文本處理利器awk
  • awk代碼結構
  • 內建變量
  • 局部變量
  • 字符串函數
    第九章 進程
  • 進程管理命令
  • init進程
  • 調度系統任務
  • 加載proc虛擬文件系統:mount -t proc proc /proc
    第十章 OpenSSH
  • 安裝openssh
  • 使用SSH登錄遠程主機(要求遠程主機正在運行sshd)
    第十一章 實用程序(日志處理程序、系統監控程序)
  • 日志清理程序
  • 系統監控程序

第一個Shell程序

以 #!解析器名稱 開頭,表示選擇哪個解釋器解釋shell腳本

source命令
使用source執行shell腳本時,不會創建子進程,而在父進程中直接執行。
當需要在程序中修改當前shell本身的環境變量時,使用source命令
source命令也可用來讀入包含函數的文件
使用方法:source fileName 或者 . fileName

export命令
export命令用於設置或顯示環境變量
使用方法:export [-fnp] [變量名稱]=[變量設置值]
-f 代表[變量名稱]中為函數名稱
-n 刪除指定的變量。變量實際上並沒有刪除,只是不會輸出到后續指令的執行環境中
-p 列出所有的shell賦予程序

env命令
env命令用於臨時改變環境變量值

unset命令
從當前shell刪除變量或函數

shell編程基礎

函數傳遞
$0 $1 $2 位置參數
$* 以一個單字符串顯示所有向腳本傳遞的參數
$@ 所有向腳本傳遞的參數
$# 傳入函數的參數個數
$$ 腳本運行的當前進程id號
$! 后台運行的最后一個進程id號
$? 顯示最后命令的退出狀態
$- 顯示shell使用的當前選項

標准輸入輸出符號
0<:標准輸入
1> 或者 >:重定向標准輸出
2>:重定向標准錯誤
&>:標准輸出和標准錯誤

編程的基本元素

獲取命令執行返回值

  1. $(命令) 如:$(date +%Y%m%d%H%M%S)
  2. 命令 如:echo 123

$((數值計算)) 獲取數值計算結果 如:$((100 - 2*100/400))

替換運算符

  1. ${varname:-word}
    如果varname存在且非null,則返回varname的值;否則,返回word
  2. ${varname:=word}
    如果varname存在且非null,則返回varname的值;否則,將varname的值設置為word,然后返回word
  3. ${varname:?message}
    如果varname存在且非null,則返回varname的值;否則打印message,並退出當前腳本
  4. ${varname:+word}
    如果varname存在且非null,則返回word;否則返回null

模式匹配運算符

  1. ${varname#pattern}
    如果模式匹配變量取值的開頭處,則刪除匹配的最短部分,並返回剩下部分
  2. ${varname##pattern}
    如果模式匹配變量取值的開頭處,則刪除匹配的最長部分,並返回剩下部分
  3. ${varname%pattern}
    如果模式匹配變量取值的結尾處,則刪除匹配的最短部分,並返回剩下部分
  4. ${varname%%pattern}
    如果模式匹配變量取值的結尾處,則刪除匹配的最長部分,並返回剩下部分
  5. ${varname/pattern/string} ${varname//pattern/string}
    將varname中匹配模式的最長部分替換為string
    第一種格式中,只有匹配的第一部分被替換,第二種格式中,varname中所有匹配的部分都被替換
    如果模式以#開頭,則必須匹配varname的開頭,如果模式以%開頭,則必須匹配varname的結尾

shift命令可以截取參數列表最左端的一個參數

type命令判斷被執行命令的來源(別名、關鍵字、函數、內置命令、外部命令)

test命令用於評估表達式,返回零(true)或者非零(false)

邏輯運算符

  1. 關於文件與目錄的偵測邏輯卷標!
    -f 常用!偵測『文件』是否存在 eg: if [ -f filename ]
    -d 常用!偵測『目錄』是否存在
    -b 偵測是否為一個『 block 文件』
    -c 偵測是否為一個『 character 文件』
    -S 偵測是否為一個『 socket 標簽文件』
    -L 偵測是否為一個『 symbolic link 的文件』
    -e 偵測『某個東西』是否存在!
  2. 關於程序的邏輯卷標!
    -G 偵測是否由 GID 所執行的程序所擁有
    -O 偵測是否由 UID 所執行的程序所擁有
    -p 偵測是否為程序間傳送信息的 name pipe 或是 FIFO
  3. 關於文件的屬性偵測!
    -r 偵測是否為可讀的屬性
    -w 偵測是否為可以寫入的屬性
    -x 偵測是否為可執行的屬性
    -s 偵測是否為『非空白文件』
    -u 偵測是否具有『 SUID 』的屬性
    -g 偵測是否具有『 SGID 』的屬性
    -k 偵測是否具有『 sticky bit 』的屬性
  4. 兩個文件之間的判斷與比較 ;例如[ test file1 -nt file2 ]
    -nt 第一個文件比第二個文件新
    -ot 第一個文件比第二個文件舊
    -ef 第一個文件與第二個文件為同一個文件( link 之類的文件)
  5. 邏輯的『和(and)』『或(or)』
    && 邏輯的 AND 的意思
    || 邏輯的 OR 的意思

運算符號
= 等於 應用於:整型或字符串比較 如果在[] 中,只能是字符串
!= 不等於 應用於:整型或字符串比較 如果在[] 中,只能是字符串
< 小於 應用於:整型比較 在[] 中,不能使用 表示字符串
大於 應用於:整型比較 在[] 中,不能使用 表示字符串
-eq 等於 應用於:整型比較
-ne 不等於 應用於:整型比較
-lt 小於 應用於:整型比較
-gt 大於 應用於:整型比較
-le 小於或等於 應用於:整型比較
-ge 大於或等於 應用於:整型比較
-a 雙方都成立(and) 邏輯表達式 –a 邏輯表達式
-o 單方成立(or) 邏輯表達式 –o 邏輯表達式
-z 空字符串
-n 非空字符串

執行反單引號(`)之間的命令,引用結果作為字符串

getopt命令用於分析命令標志和參數

正則表達式

元字符
^ 鎖定行或字符串的開始
$ 鎖定行或字符串的結尾
當^和$結合使用時,意味着模式必須匹配整個串
. 匹配除了換行符以外的任意字符
[...] 方括號表達式,匹配括號內任意一個字符;如果^符號位於方括號的開始,則不匹配方括號中的任意字符
\ 轉義字符,用於打開或關閉后續字符的特殊含義
x{m,n} 區間表達式,匹配x字符出現的次數區間
? 匹配前面正則表達式的零個或一個實例

  • 匹配前面正則表達式的一個或多個實例
  • 匹配零個或多個前面字符
    | 匹配前面或后面的正則表達式
    () 匹配括號括起來的正則表達式
    \b 單詞鎖定符,代表單詞的開頭和結尾,即單詞的分界處
    \B 匹配兩個單詞組成字符間的空字符串
    < > 分別匹配單詞開頭和單詞結尾
    \n 換行符
    \d 匹配一位數字
    \w 匹配文字和數字字符
    \W 匹配一個或多個非單詞字符

POSIX字符集
[:alnum:] 數字字符
[:alpha:] 字母字符
[:blank:] 空格與制表符
[:cntrl:] 控制字符
[:digit:] 數字字符
[:graph:] 非空格
[:lower:] 小寫字母
[:print:] 可顯示的字符
[:punct:] 標點符號字符
[:space:] 空白(whitespace)字符
[:upper:] 大寫字母
[:xdigit:] 十六進制數字
[. .] 排序符號,如[.cn.]表示cn字符序列,而單獨的c和n都不行
[= =] 等價字符集

后向引用:匹配之前正則表達式使用(和)括起來選定之后引用的模式,使用\1~\9來引用選定的模式
如:(go).*\1 匹配一行中前后出現兩個go

交替、分組
交替|:在不同序列之間用管道符號隔開
分組():讓元字符修飾前置字符串
如:(man|woman)+ 匹配一個或多個man或者woman字符串

基本文本處理

排序文本
sort命令用於排序文件,對已排序的文件進行合並,並檢查文件以確定它們是否已排序

文本去重
uniq命令用於文本去重(在使用uniq命令前,先使用sort命令,使所有重復行相鄰)

統計文本
wc命令用於統計文本行數、字數以及字符數

打印和格式化輸出
pr命令用於將文本轉換成適合打印的文件
fmt命令用於編排文本文件
fold命令限制文本寬度

提取文本開頭和結尾
head命令提取文件開頭
tail命令提取文件結尾

字段處理
cut命令用於從一個文本文件或者文本流中提取文本列
join命令用於根據指定欄位,找到兩個文件中指定欄位內容相同的行,將它們合並,並根據要求的格式輸出內容

文本替換
tr命令用於替換字符

Linux下的配置文件
/etc/group 用戶組定義
/etc/passwd 用戶信息定義
/etc/inittab init的配置文件,在Linux啟動時扮演重要角色
/etc/shadow 用戶密碼的存放地址
/etc/crontab cron(定期執行命令的程序)的配置文件
/etc/fstab 文件系統信息

文件和文件系統

文件
ls命令用於列出文件
chown命令用於改變文件的所有者
chgrp命令用於改變文件的用戶組
umask命令用於指定哪些權限在新文件的默認權限中被刪除
chmod命令用於修改文件權限
touch命令更新文件的訪問和修改時間
find命令用於尋找文件
xargs命令用於遍歷處理文件
comm命令用於比較兩個已排序文件之間的差異
diff命令用於比較兩個文件之間的差異

文件系統
fdisk命令用於查看/修改系統的分區表
mkfs命令用於創建文件系統
mount命令用於加載文件系統到指定的加載點
umount命令用於卸載已經加載的文件系統
df命令用於顯示當前掛載情況

流編輯器(sed)

工作地址范圍

  1. 行地址 如:刪除第一行 sed -e '1d' filePath
  2. 行范圍地址 如:打印1到5行 sed -n -e '1,5p' filePath
  3. 正則表達式地址 如:打印所有以開頭的注釋行 sed -n -e '/^#/p' filePath
  4. 兩個用逗號分開的正則表達式之間的地址 如:打印從包含'BEGIN'的行開始,並且包含'END'的行結束的文本塊 sed -n -e '/BEGIN/,/END/p' filePath

命令

  1. d 刪除
  2. p 打印
  3. s/// 替換
  4. = 打印行號
  5. i 插入
  6. a 追加

[:特殊字符:]用於匹配特俗字符

文本處理利器(awk)

awk代碼結構

  1. 處理輸入前的初始化
    BEGIN{
    ....
    }
  2. 處理輸入過程
    [ 條件 ] {
    ...
    }
  3. 處理完所有輸入后的掃尾工作
    END{
    ...
    }

內建變量
FILENAME 當前輸入文件的名稱
FS 字段分隔符(支持正則表達式),默認為空格
OFS 輸出字段分隔字符,默認為空格
ORS 輸出記錄分隔字符,默認為\n
RS 輸入記錄分隔字符
NF 當前記錄的字段數
NR 在工作中的記錄數
FNR 當前輸入文件的記錄數

局部變量:列在函數參數列表中並且在字首前置一些額外的空白 如: add(x,y, sum) {}

字符串函數
sub(/reg/, newsubstr, str) 只替換第一個匹配字符串
gsub(/reg/, newsubstr, str) 將字符串str中所有符號/reg/正則的子串替換為字符串newsubstr
index(str, substr) 返回子串substr在串str中的索引
length(str) 返回字符串的長度
match(str, /reg/) 如果在串str中找到正則/reg/匹配的串,則返回出現的位置,未找到則返回0
split(str, array, sep) 使用分隔符sep把字符串分解成數組array
substr(str, position[, length]) 返回str中從position開始的length個字符
toupper(str) 對字符進行大小寫轉換
sprintf("format", expr) 對expr使用printf格式說明

進程

進程管理命令
fork函數用於創建進程
ps命令用於查看系統正在運行的進程
top命令用於查看系統一段時間進程的動態信息
pstree命令用於打印進程樹形結構
Ctrl+C快捷鍵用於中斷前台進程
Ctrl+\快捷鍵用於殺死前台進程
Ctrl+Z快捷鍵用於掛起前台進程
bg命令用於將掛起進程轉換為后台進程
fg命令用於將后台進程轉換為前台進程
jobs命令用於顯示當前shell的進程狀況
kill命令用於向指定進程發送信號
init命令用於進程初始化工具,可切換運行等級

init進程

  1. /etc/inittab init程序讀取的配置文件
    基本格式為 id:runlevels:action:process
    • id為1~2個字符,配置行的唯一標識,在配置文件中不能重復
    • runlevels(運行等級)取值如下
      • 等級0表示:表示關機(千萬不能把initdefault 設置為0)
      • 等級1表示:單用戶模式
      • 等級2表示:無網絡連接的多用戶命令行模式
      • 等級3表示:有網絡連接的多用戶命令行模式
      • 等級4表示:不可用
      • 等級5表示:帶圖形界面的多用戶模式
      • 等級6表示:重新啟動(千萬不要把initdefault 設置為6)
    • action取值如下
      • respawn 啟動並監視第4項指定的process,若process終止則重啟它
      • wait 執行第4項指定的process,並等待它執行完畢
      • once 執行第4項指定的process
      • boot 不論在哪個執行等級,系統啟動時都會運行第4項指定的process
      • bootwait 不論在哪個執行等級,系統啟動時都會運行第4項指定的process,且一直等它執行完備
      • off 關閉任何動作,相當於忽略該配置行
      • ondemand 進入ondemand執行等級時,執行第4項指定的process
      • initdefault 系統啟動后進入的執行等級,該行不需要指定process
      • sysinit 不論在哪個執行等級,系統會在執行boot 及bootwait之前執行第4項指定的process
      • powerwait 當系統的供電不足時執行第4項指定的 process,且一直等它執行完畢
      • powerokwait 當系統的供電恢復正常時執行第4項指定的process,且一直等它執行完畢
      • powerfailnow 當系統的供電嚴重不足時執行第4項指定的process
      • ctrlaltdel 用戶按下【Ctrl+Alt+Del】時執行的操作
      • kbrequest 當用戶按下特殊的組合鍵時執行第4項指定的process,此組合鍵需在keymaps文件定義
    • process為所要執行的shell命令。任何合法的shell語法均適用於該字段。
  2. /etc/rc.d/rcX.d文件(X代表運行等級)含有各個運行等級服務啟動和終止配置
    /etc/rc.d/init.d目錄下存放着對應運行等級的服務腳本
  3. 修改配置/etc/inittab后馬上生效:kill -1 1 或者 init q

調度系統任務

  1. crontab命令用於調度重復性的任務
    控制訪問的文件:cron.allow、cron.deny
  2. at命令用於調度只執行一次的任務
    控制訪問的文件:at.deny

加載proc虛擬文件系統:mount -t proc proc /proc

SSH

安裝openssh

  • 替換阿里雲的源
    echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories
    echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories

  • 同步時間
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

  • 更新源、安裝openssh 並修改配置文件、生成key、啟動sshd服務
    apk update &&
    apk add --no-cache openssh-server tzdata &&
    sed -i "s/#PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config &&
    ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key &&
    ssh-keygen -t ecdsa -P "" -f /etc/ssh/ssh_host_ecdsa_key &&
    ssh-keygen -t ed25519 -P "" -f /etc/ssh/ssh_host_ed25519_key &&
    /usr/sbin/sshd -D

使用SSH登錄遠程主機(要求遠程主機正在運行sshd)

  • 基於口令的登錄方法
    ssh -l 登錄賬號 遠程主機

  • 基於密鑰對的登錄方法

    • 生成密鑰對:ssh-keygen -d
    • 把公鑰上傳到服務器上(/.ssh/id_dsa.pub、/.ssh/authorized_keys)
    • 測試自動登錄:ssh [登錄賬號@]遠程主機 (需要輸入第一步輸入的passphrase,若想不輸入passphrase可以考慮ssh-agent)

實用程序

日志清理程序
把以下代碼保存為log_clean.sh文件中即可使用
內含操作:備份重要的日志、限制日志目錄大小、清理老舊日志


# maximum log size
alarmrate=500
# the max size file can reach
file_max_size=5
# this is the directory where fresh logs are originally written
working_dir=/mnt/soho_storage/log
# this is the frequency our program runs
SLEEPTIME=5

# append year.month.day and timestamp to log filename
filenameConvert()
{
    timestamp=$(date +%Y%m%d%H%M%S)
    timestamp=`echo $timestamp`
    RETVAL=$1.$timestamp
}

# search dir to fetch the oldest log
searchdir()
{
    oldestlog=`ls -rt | head -n 1 | awk '{print $1}'`
}

# this function clean old logs under working dir if it reaches it's size limitation
clear_old_log_under_working_dir()
{
    cd $working_dir
    while true
    do
        logsize=`du -ms $working_dir | awk '{print $1}'`
        if [ $logsize -gt $alarmrate ]
        then
            searchdir
            rm -rf $oldestlog
        else
            break;
        fi
    done
}

# this is the main process of our log backup activity
backuplog_process()
{
    cd $log_ram_dir
    for i in * 
    do
        file_size=`du -m $i | awk '{print $1}'`
        # need to backup log file
        case $i in access.log | error.log | apcupsd.events | soho.log)
            if [ ! -d $working_dir ]
            then
                mkdir -p $working_dir
            fi
            if [ file_size -gt file_max_size ]
            then
                filenameConvert $i
                cp $log_ram_dir/$i $working_dir/$RETVAL
                echo "" > $log_ram_dir/$i
                clear_old_log_under_working_dir
            fi
            ;;
        *)
            if [ file_size -gt file_max_size ]
            then
                echo "" > $log_ram_dir/$i
            fi
    done
}

while true
do
    backuplog_process
    sleep $SLEEPTIME
done

系統監控程序
把以下代碼保存為system_monitor.sh文件中即可使用
內含操作:監控內存、硬盤、CPU、進程,形成狀態報告


# maximum ratio of memory usage
mem_quota=80
# hard disk
hd_path=/dev/sda1
# maximum ratio of hard disk usage
hd_quota=80
# maximum ratio of cpu usage
cpu_quota=80
# time gap between two times fetching cpu status
time_gap=60
# generate report every 10 minutes
runtime_gap=60

# fetch the ratio of memory usage
# @return 1: if larger than $mem_quota
          0: if less than $mem_quota
watch_memory()
{
    mem_total=`cat /proc/meminfo | grep MemTotal | awk '{print $2}'`
    mem_free=`cat /proc/meminfo | grep MemFree | awk '{print $2}'`
    mem_usage=$((100-mem_free*100/mem_total))
    if [ $mem_usage -gt $mem_quota ]
    then
        mem_message="ALARM!! The memory usage is $mem_usage%!!"
        return 1
    else
        return 0
    fi
}

# fetch the top 10 most wasting memory process
proc_memory_top10()
{
    mem_busiest=`ps aux | sort -nk 4r | head -n 11`
}

# fetch the ratio of hard disk usage
# @return 1: if larger than $hd_quota
          0: if less than $hd_quota    
watch_hd()
{
    hd_usage=`df | grep $hd_path | awk '{print $5}' | sed 's/%//g'`
    if [ $hd_usage -gt $hd_quota ]
    then
        hd_message="ALARM!! The hard disk usage is $hd_usage%!!"
        return 1
    else
        return 0
    fi
}

# fetch cpu status at a time point
# format used unused
get_cpu_info()
{
    cat /proc/stat | grep -i "^cpu[0-9]\+" | awk '{used+=$2+$3+$4;unused+=$5+$6+$7+$8} END{print used,unused}'
}

# fetch the ratio of cpu usage
# fetch cpu stat two times, with time gap, then calculate the average status
# @return 1: if larger than $cpu_quota
          0: if less than $cpu_quota    
watch_cpu()
{
    time_point_1=`get_cpu_info`
    sleep $time_gap
    time_point_2=`get_cpu_info`
    cpu_usage=`echo $time_point_1 $time_point_2 | awk '{used=$3-$1;total+=$3+$4-$2-$1}';print $used*100/total`
    if [ $cpu_usage -gt $cpu_quota ]
    then
        cpu_message="ALARM!! The cpu usage is over $cpu_usage%!!"
        return 1
    else
        return 0
    fi
}

# fetch the top 10 busiest processes
proc_cpu_top10()
{
    proc_busiest=`ps aux | sort -nk 3r | head -n 11`
}

while true
do
    # report content
    report=""
    # memory monitor
    if [ `watch_memory` -eq 1 ]
    then
        report=$report'\n'$mem_message
        proc_memory_top10
        report=$report'\n'$mem_busiest
    fi
    # hard disk monitor
    if [ `watch_hd` -eq 1 ]
    then
        report=$report'\n'$hd_message
    fi
    # cpu monitor
    if [ `watch_cpu` -eq 1 ]
    then
        report=$report'\n'$cpu_message
        proc_cpu_top10
        report=$report'\n'$proc_busiest
    fi
    # feedback report
    if [ -n $report ]
    then
        # todo...
    fi
    # sleep time
    sleep $((runtime_gap-time_gap))
done


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM