shell程式設計基礎

Linux小菜鸟發表於2024-07-10

shell指令碼程式設計基礎

【1】、shell概述

  • shell是一門程式語言,如:C、C++、java、PHP、python Go等

  • 語言分類

    程式設計型語言:C、C++、Go等為編譯型語言。程式執行需要提前編譯,編譯語言都有編譯器

    解釋型語言:shell、PHP、python等為解釋型語言,程式在執行時不需要提前編譯,一邊執行,一邊解釋,每種解釋型語言都有直譯器

  • shell語言支援大部分程式語言都具備的功能:if判斷、for迴圈、函式、陣列、加減乘除、邏輯運算等

  • 指令碼的本質就是命令的堆積

【2】、shell編寫規範

系統直譯器

[root@xu script]# cat /etc/shells
/bin/sh		#Unix直譯器
/bin/bash	#Linux預設直譯器
/usr/bin/sh
/usr/bin/bash

這就是一個簡單的shell script,在我們編寫完成後,要給script加上可執行的許可權

#!/bin/bash
#首先要進行環境指定,指定編譯器,一般我們會使用/bin/bash,標準直譯器
# '#'表示註釋、‘#!’表示指定直譯器

#功能:輸出hello world
echo hello world

在指令碼中不能出現互動式命令

#例如建立使用者並且給使用者設定密碼
#!/bin/bash
#creat user
useradd zhanghaowei
echo 123 | passwd --stdin zhanghaowei
#我們使用設定密碼的方式採用非互動式的方式

【3】、如何寫好一個shell指令碼

  • 明確任務需求
  • 按照需求整理好每一個步驟,先後順序
  • 執行指令碼,並根據執行結果排除錯誤
  • 最佳化指令碼並達到最後的結果
  • 指令碼程式規範
    • 宣告直譯器(shebang)

      • !/bin/bash

    • 註釋

      • 對指令碼的描述
    • 執行指令

      • Linux命令

【4】、編寫指令碼

  • 自動配置本地yum倉庫的指令碼

    [BaseOS]
    name=local_BaseOS
    baseurl=file:///mnt/cdrom/BaseOS
    enable=1
    gpgcheck=0
    
    [AppStream]
    name=local_AppStream
    baseurl=file:///mnt/cdrom/AppStream
    enable=1
    gpgcheck=0
    EOF
    
    sleep 2
    echo "======永久掛載光碟機======"
    echo "/dev/cdrom        /mnt/cdrom      iso9660 defaults        0 0" >> /etc/fstab
    
    sleep 2
    echo "======檢查軟體包數量======"
    yum list | wc -l
    

【5】、指令碼的執行方式

  • 方式一:賦予指令碼執行許可權後,可用絕對路徑和相對路徑執行

  • 方式二:呼叫直譯器執行指令碼檔案,會開啟jie'shi'q

  • 方式三:source和 . :執行指令碼檔案

  • #絕對路徑執行指令碼
    [root@xu script]# /script/hello.sh 
    hello world
    #相對路徑執行指令碼
    [root@xu script]# ./hello.sh 
    hello world
    
  • #呼叫直譯器執行指令碼,呼叫直譯器時可以不給執行許可權
    [root@xu script]# bash /script/hello.sh 
    hello world
    
  • 父子shell問題

    #我們知道指令碼的執行是透過呼叫bash直譯器去執行,我們透過sshd連線一個會話可以存在多個bash程序
    ─sshd(1065)───sshd(1896)───sshd(1900)───bash(1901)───bash(2093)───bash(2114)───pstree(2135)
    #透過pstree我們可以看到當前session存在多個bash環境,都是由systemd程序建立出來的,而我們現在處於bash(2114)環境中,如果此時我們執行指令碼採用的環境時bash(2114)環境。
    #如果我們使用路徑(source)去執行指令碼和使用bash去執行指令碼就會產生區別
    #採用.(source)去執行指令碼,是在當前的bash環境下執行的,而使用bash或path去執行指令碼則是在當前的bash環境下建立出一個子bash去執行指令碼,並切換當前環境為新的指令碼
    

【6】、變數

  • shell中變數型別只有string,沒有別的;比方說在python中變數是有不同的型別的(list、dic、str。。。),但是在shell中只有str.

  • 變數的存在讓指令碼變得更加靈活,能夠應對不同的場景

  • 語法:變數名="變數值"

  • $:呼叫變數

    [root@xu ~]# name=$(hostname)
    [root@xu ~]# echo $name
    xu
    

1、變數分類

(1)、系統定義變數

# 系統自定義的系統變數
[root@moudle01 17:23:53  /script]# echo $PWD
/script
[root@moudle01 17:23:59  /script]# echo $SHELL
/bin/bash
[root@moudle01 17:24:05  /script]# echo $HOME
/root
[root@moudle01 17:24:14  /script]# echo $USER
root
[root@moudle01 17:24:51  /script]# echo $UID
0

(2)、位置變數和預定義變數

在shell中也存在著和C語言相似的引數傳遞問題

只不過在shell中的引數問題是透過命令去完成的,我們在執行一個shell scrip時可以寫上引數,將引數傳遞進shell script,同時在shell中去接收透過命令傳入的引數。

位置引數:在執行指令碼的命令中寫上的引數就是位置引數,從1開始

在接收時可以使用以下識別符號

  • $0:提取檔名(用source執行指令碼時,不顯示檔名而是直譯器名字)
  • ${1}:提取位置引數為1的值
  • ...
  • ${20}: 提取位置引數為20的值
  • $*:接收所有引數
  • $@:接收所有引數
  • $$:提取當前程序的PId
  • $?:上一條命令的執行結果(上一個程式/命令的返回狀態碼)
  • $#:所有引數個數
#!/bin/bash
echo $1
echo $2
echo $3
echo $0
echo $*
echo $#
echo $$
echo $?
[root@moudle01 21:10:40  /script]# bash test04.sh  a b c d
a
b
c
test04.sh
a b c d
4
2728
0
[root@moudle01 21:18:33  /script]# kjasdhfsdlgasgasgadfg
-bash: kjasdhfsdlgasgasgadfg: command not found
[root@moudle01 21:18:45  /script]# echo $?
127
[root@moudle01 21:18:49  /script]# echo $?
0

建立使用者tom,密碼123

#!/bin/bash
  
useradd $1
echo $2  | passwd --stdin $1

2、env和set

env:所有環境變數

set:所有變數

[root@moudle01 21:30:10  /script]# abcd=6666666
[root@moudle01 21:30:55  /script]# set | grep abcd
abcd=6666666
[root@moudle01 21:31:00  /script]# env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

3、常用的特殊符號

  • “”:雙引號,引用整體,界定範圍

  • ‘’:單引號,引用整體,並且取消所有特殊字元含義,界定範圍

    [root@moudle01 21:32:13  /script]# a="  a"
    [root@moudle01 21:36:17  /script]# echo "$a"
      a
    # 如果有特殊符號,定義時單引號和雙引號都行,在引用時只能使用雙引號
      [root@moudle01 21:36:25  /script]# echo '$a'
    $a
    
    
  • $[]、$(())、expr:四則運算

    [root@xu script]# echo $[1+1]
    2
    [root@xu script]# echo $[2*3]
    6
    [root@xu script]# echo $[2-3]
    -1
    [root@xu script]# echo $[6/3]
    2
    [root@xu script]# echo $[10%3]
    1
    # expr 運算子號兩側必須加空格
    [root@moudle01 16:13:56  ~]# expr 1 + 1
    2
    
  • $():取命令結果作為引數

  • ``:反撇號,將命令的輸出結果作為引數

    [root@moudle01 21:40:57  /script]# date
    Wed Jun 12 21:41:07 CST 2024
    [root@moudle01 21:38:26  /script]# a=`date`
    [root@moudle01 21:40:54  /script]# echo $a
    Wed Jun 12 21:40:54 CST 2024
    [root@moudle01 21:41:07  /script]# x=$(ls)
    [root@moudle01 21:42:37  /script]# echo $x
    abc rsync_inotify.sh test01.sh test02.sh test03.sh test04.sh test05.sh
    

4、read

互動式輸入變數

[root@moudle01 21:48:04  /script]# read -p "請輸入:" a
請輸入:123
[root@moudle01 21:48:47  /script]# echo $a
123
#!/bin/bash
read -p "請輸入要建立的使用者名稱:" a
useradd $a
read -p "請輸入使用者的密碼:" p
echo $p  | passwd --stdin $a

[root@moudle01 21:50:41  /script]# source test05.sh 
請輸入要建立的使用者名稱:xxx
請輸入使用者的密碼:123
Changing password for user xxx.
passwd: all authentication tokens updated successfully

5、遮蔽回顯

在敲完之後,你在後面輸入的所有內容都看不到

[root@moudle01 21:51:16  /script]# stty -echo

顯示回顯

[root@moudle01 21:51:16  /script]# stty echo

寫入建立使用者的指令碼,讓密碼輸入更加安全

#!/bin/bash
read -p "請輸入要建立的使用者名稱:" a
useradd $a
stty -echo
read -p "請輸入使用者的密碼:" p
stty echo
echo ""
echo $p  | passwd --stdin $a

[root@moudle01 21:59:24  /script]# source test05.sh 
請輸入要建立的使用者名稱:aaa
請輸入使用者的密碼:
Changing password for user aaa.
passwd: all authentication tokens updated successfully.

6、export命令

  • 環境變數(全域性變數):系統在全域性生效的變數

    普通變數(區域性變數):只針對區域性環境生效,如當前某一個login.bash指令碼生效

    全域性變數和區域性變數就是相對於父子shell(bash)。環境變數(全集變數)就是父shell也可以使用,普通變數(區域性變數)只有子shell可以使用

  • 變數生存週期

    1、永久變數:寫入檔案,反覆讀取、載入,讓其永久生效,如/etc/profile的PATH修改

    2、臨時變數:如命令列export定義的一個變數儲存資料,關閉shell後失效

  • 在定義變數時有兩種選擇

  • 1、加export:在父shell中定義的變數可以在子shell中去使用,也就是全域性變數

    • [root@xu ~]# export name="xuruizhao"
      [root@xu ~]# echo ${name}
      xuruizhao
      [root@xu ~]# bash
      Skill is acquired through repeated practice, and practice makes perfect.
      [root@xu ~]# bash
      Skill is acquired through repeated practice, and practice makes perfect.
      [root@xu ~]# echo ${name}
      xuruizhao
      

    2、不加export:只對當前的shell有效,子shell看不到,也就是區域性變數

    • [root@xu ~]# name="hello world"
      [root@xu ~]# echo ${name}
      hello world
      [root@xu ~]# bash
      #我們切換了bash環境,也就是進入了子shell,由於沒有加export子shell中是不能使用父shell中定義的變數
      Skill is acquired through repeated practice, and practice makes perfect.
      [root@xu ~]# echo ${name}
                              
      [root@xu ~]# 
      

7、let定義變數

使用let定義變數,可以在定義時進行計算

[root@moudle01 22:13:42  /script]# let a=1+1
[root@moudle01 22:14:21  /script]# echo $a
2
[root@moudle01 22:16:30  /script]# let a+=3
[root@moudle01 22:16:35  /script]# echo $a
5

8、bc命令

上面的運算均不支援小數

bc命令支援小數

[root@moudle01 16:13:58  ~]# echo "1.1+1" | bc
2.1

9、變數定義初值

#!/bin/bash
  
read -p "請輸入使用者名稱" username
if [ -z $username ];then
    echo "使用者名稱不能為空" && exit
fi
useradd $username
read -p "請輸入密碼" pass
echo ${pass:-123456} | passwd --stdin $username
[root@moudle01 11:37:41  /script]# bash user_add02.sh 
請輸入使用者名稱
使用者名稱不能為空

【7】、變數測試

兩種測試格式

test

[]

1、字串

(1)、陣列測試

== 判斷是否相同

! = 判斷兩側是否不等

[root@moudle01 16:29:46  ~]# a=1
[root@moudle01 16:29:51  ~]# b=2
[root@moudle01 16:29:52  ~]# [ $a != $b ]
[root@moudle01 16:30:04  ~]# echo $?
0

如果一個變數值為空,我們在進行比較時,

1、可以加上雙引號

2、-z:判斷變數是否為空

​ ! -Z/-n:判斷變數是否非空

 
[root@moudle01 16:33:11  ~]# [ a == "$c" ]
[root@moudle01 16:33:23  ~]# echo $?
1
[root@moudle01 16:33:27  ~]# [ -z "$c" ]
[root@moudle01 16:35:13  ~]# echo $?
0
[root@moudle01 16:35:16  ~]# [ -z "$a" ]
[root@moudle01 16:35:22  ~]# echo $?
1
[root@moudle01 16:38:16  ~]# c=123
[root@moudle01 16:38:23  ~]# [ -n "$c" ] 
[root@moudle01 16:38:26  ~]# echo $?
0

(2)、邏輯測試

&&與

||或

#!/bin/bash
  
[ $USER == root ] || echo "非管理員禁止執行該指令碼" && exit
yum -y install vsftpd > /dev/null
systemctl start vsftpd
systemctl enable vsftpd
# 只有root使用者可以執行此指令碼
#!/bin/bash
  
[ $USER != root ] && echo "非管理員禁止執行" && exit2
yum -y install vsftpd > /dev/null
systemctl start vsftpd
systemctl enable vsftpd
[root@moudle01 16:57:25  /script]# ls a ||  ls b && ls c || ls && ls /etc/yum.repos.d/
a
c
bak  epel  local_yum_house.repo

2、數字

-eq:相等

-ne:不相等

-gt:大於

-ge:小於等於

-lt:小於

-le:小於等於

#!/bin/bash
  
[ $# -ne 2 ] && echo "輸入變數數量錯誤" && exit
useradd $1
echo $2  | passwd --stdin $1

3、檔案

-e:判斷檔案是否存在,不關心檔案型別

-f:判斷檔案是否存在,必須是普通檔案

-d:判斷檔案是否存在,必須是目錄

[root@moudle01 17:19:49  /script]# [ -e a ]
[root@moudle01 17:20:00  /script]# echo $?
0
[root@moudle01 17:20:03  /script]# [ -d a ]
[root@moudle01 17:20:09  /script]# echo $?
1
[root@moudle01 17:20:10  /script]# [ -f x ]
[root@moudle01 17:20:21  /script]# echo $?
1

-r:判斷當前使用者對檔案是否有讀許可權(對root無效,只針對普通使用者)

-w:判斷當前使用者對檔案是否有寫許可權(對root無效,只針對普通使用者)

-x:判斷當前使用者對檔案是否有x許可權

[root@moudle01 17:21:45  /script]# [ -r a ]
[root@moudle01 17:22:02  /script]# echo $?
0
[root@moudle01 17:22:05  /script]# [ -w a ]
[root@moudle01 17:22:09  /script]# echo $?
0
[root@moudle01 17:22:10  /script]# [ -x a ]
[root@moudle01 17:22:17  /script]# echo $?
1

4、編寫指令碼

每隔兩分鐘檢查伺服器的賬戶數量,如果發現增加,則給管理員發郵件

#!/bin/bash
  
num=$(cat /etc/passwd | wc -l)
[ num -gt 38 ] && echo "使用者增加" | mail -s "user_info" root

# 編寫計劃任務
*/2 * * * * /script/test02.sh

相關文章