天天看點

Linux-ShellLinux-Shell

文章目錄

  • Linux-Shell
    • 概論
    • 注釋
    • 變量
    • 預設變量
    • 數組
    • expr指令
    • read指令
    • echo指令
    • printf指令
    • test指令與判斷符号[]
    • 判斷語句
    • 循環語句
    • 函數
    • exit指令
    • 檔案重定向
    • 引入外部腳本

Linux-Shell

概論

概況

shell是我們通過指令行與作業系統溝通的語言。

shell腳本可以直接在指令行中執行,也可以将一套邏輯組織成一個檔案,友善複用。

AC Terminal中的指令行可以看成是一個“shell腳本在逐行執行”。

Linux中常見的shell腳本有很多種,常見的有:

  • Bourne Shell(

    /usr/bin/sh

    /bin/sh

    )
  • Bourne Again Shell(

    /bin/bash

    )
  • C Shell(

    /usr/bin/csh

    )
  • K Shell(

    /usr/bin/ksh

    )
  • zsh

Linux系統中一般預設使用bash,是以接下來講解bash中的文法。

檔案開頭需要寫

#! /bin/bash

,指明bash為腳本解釋器。

學習技巧

不要死記硬背,遇到含糊不清的地方,可以在AC Terminal裡實際運作一遍

腳本示例

建立一個

test.sh

檔案,内容如下:

#! /bin/bash
echo "Hello World!"
           

運作方式

作為可執行檔案

[email protected]:~$ chmod +x test.sh  # 使腳本具有可執行權限
[email protected]:~$ ./test.sh  # 目前路徑下執行
Hello World!  # 腳本輸出
[email protected]:~$ /home/acs/test.sh  # 絕對路徑下執行
Hello World!  # 腳本輸出
[email protected]:~$ ~/test.sh  # 家目錄路徑下執行
Hello World!  # 腳本輸出
           

用解釋器執行

[email protected]:~$ bash test.sh
Hello World!  # 腳本輸出
           

注釋

單行注釋

每行中

#

之後的内容均是注釋。

# 這是一行注釋

echo 'Hello World'  #  這也是注釋
           

多行注釋

格式:

:<<EOF
第一行注釋
第二行注釋
第三行注釋
EOF
           

其中

EOF

可以換成其它任意字元串。例如:

:<<abc
第一行注釋
第二行注釋
第三行注釋
abc

:<<!
第一行注釋
第二行注釋
第三行注釋
!
           

變量

定義變量

定義變量,不需要加

$

符号,例如:

name1='yxc'  # 單引号定義字元串
name2="yxc"  # 雙引号定義字元串
name3=yxc    # 也可以不加引号,同樣表示字元串
           

使用變量

使用變量,需要加上

$

符号,或者

${}

符号。花括号是可選的,主要為了幫助解釋器識别變量邊界。

name=yxc
echo $name  # 輸出yxc
echo ${name}  # 輸出yxc
echo ${name}acwing  # 輸出yxcacwing
           

隻讀變量

使用

readonly

或者

declare

可以将變量變為隻讀。

name=yxc
readonly name
declare -r name  # 兩種寫法均可

name=abc  # 會報錯,因為此時name隻讀
           

删除變量

unset

可以删除變量。

name=yxc
unset name
echo $name  # 輸出空行
           

變量類型

  1. 自定義變量(局部變量)

    子程序不能通路的變量

  2. 環境變量(全局變量)

    子程序可以通路的變量

自定義變量改成環境變量:

[email protected]:~$ name=yxc  # 定義變量
[email protected]:~$ export name  # 第一種方法
[email protected]:~$ declare -x name  # 第二種方法
           

環境變量改為自定義變量:

[email protected]:~$ export name=yxc  # 定義環境變量
[email protected]:~$ declare +x name  # 改為自定義變量
           

字元串

字元串可以用單引号,也可以用雙引号,也可以不用引号。

單引号與雙引号的差別:

  • 單引号中的内容會原樣輸出,不會執行、不會取變量;
  • 雙引号中的内容可以執行、可以取變量;
name=yxc  # 不用引号
echo 'hello, $name \"hh\"'  # 單引号字元串,輸出 hello, $name \"hh\"
echo "hello, $name \"hh\""  # 雙引号字元串,輸出 hello, yxc "hh"
           

擷取字元串長度

name="yxc"
echo ${#name}  # 輸出3
           

提取子串

name="hello, yxc"
echo ${name:0:5}  # 提取從0開始的5個字元
           

預設變量

檔案參數變量

在執行shell腳本時,可以向腳本傳遞參數。

$1

是第一個參數,

$2

是第二個參數,以此類推。特殊的,

$0

是檔案名(包含路徑)。例如:

建立檔案

test.sh

#! /bin/bash

echo "檔案名:"$0
echo "第一個參數:"$1
echo "第二個參數:"$2
echo "第三個參數:"$3
echo "第四個參數:"$4
           

然後執行該腳本:

[email protected]:~$ chmod +x test.sh 
[email protected]:~$ ./test.sh 1 2 3 4
檔案名:./test.sh
第一個參數:1
第二個參數:2
第三個參數:3
第四個參數:4
           

其它參數相關變量

參數 作用

$#

代表檔案傳入的參數個數,如上例中值為4

$*

由所有參數構成的用空格隔開的字元串,如上例中值為

"$1 $2 $3 $4"

[email protected]

每個參數分别用雙引号括起來的字元串,如上例中值為

"$1" "$2" "$3" "$4"

$$

腳本目前運作的程序ID

$?

上一條指令的退出狀态(注意不是stdout,而是exit code)。0表示正常退出,其他值表示錯誤

$(command)

傳回

command

這條指令的stdout(可嵌套)

command

傳回

command

這條指令的stdout(不可嵌套)

數組

數組中可以存放多個不同類型的值,隻支援一維數組,初始化時不需要指明數組大小。

數組下标從0開始。

定義

數組用小括号表示,元素之間用空格隔開。例如:

也可以直接定義數組中某個元素的值:

array[0]=1
array[1]=abc
array[2]="def"
array[3]=yxc
           

讀取數組中某個元素的值

格式:

例如:

array=(1 abc "def" yxc)
echo ${array[0]}
echo ${array[1]}
echo ${array[2]}
echo ${array[3]}
           

讀取整個數組

格式:

${array[@]}  # 第一種寫法
${array[*]}  # 第二種寫法
           

例如:

array=(1 abc "def" yxc)

echo ${array[@]}  # 第一種寫法
echo ${array[*]}  # 第二種寫法
           

數組長度

類似于字元串

${#array[@]}  # 第一種寫法
${#array[*]}  # 第二種寫法
           

例如:

array=(1 abc "def" yxc)

echo ${#array[@]}  # 第一種寫法
echo ${#array[*]}  # 第二種寫法
           

expr指令

expr

指令使用者求表達式的值,格式為:

expr 表達式
           

表達式說明:

  • 用空格隔開每一項
  • 用反斜杠放在shell特定的字元前面(發現表達式運作錯誤時,可以試試轉義)
  • 對包含空格和其他特殊字元的字元串要用引号括起來
  • expr會在

    stdout

    中輸出結果。如果為邏輯關系表達式,則結果為真,

    stdout

    為1,否則為0。
  • expr的

    exit code

    :如果為邏輯關系表達式,則結果為真,

    exit code

    為0,否則為1。

字元串表達式

  • length STRING

    傳回

    STRING

    的長度
  • index STRING CHARSET

    CHARSET

    中任意單個字元在

    STRING

    中最前面的字元位置,下标從1開始。如果在

    STRING

    中完全不存在

    CHARSET中的

    字元,則傳回0。
  • substr STRING POSITION LENGTH

    傳回

    STRING

    字元串中從

    POSITION

    開始,長度最大為

    LENGTH

    的子串。如果

    POSITION

    LENGTH

    為負數,0或非數值,則傳回空字元串。

示例:

str="Hello World!"

echo `expr length "$str"`  # ``不是單引号,表示執行該指令,輸出12
echo `expr index "$str" aWd`  # 輸出7,下标從1開始
echo `expr substr "$str" 2 3`  # 輸出 ell
           

整數表達式

expr

支援普通的算術操作,算術表達式優先級低于字元串表達式,高于邏輯關系表達式。

  • + -

    加減運算。兩端參數會轉換為整數,如果轉換失敗則報錯。
  • * / %

    乘,除,取模運算。兩端參數會轉換為整數,如果轉換失敗則報錯。
  • ()

    可以該表優先級,但需要用反斜杠轉義

示例:

a=3
b=4

echo `expr $a + $b`  # 輸出7
echo `expr $a - $b`  # 輸出-1
echo `expr $a \* $b`  # 輸出12,*需要轉義
echo `expr $a / $b`  # 輸出0,整除
echo `expr $a % $b` # 輸出3
echo `expr \( $a + 1 \) \* \( $b + 1 \)`  # 輸出20,值為(a + 1) * (b + 1)
           

邏輯關系表達式

  • |

    如果第一個參數非空且非0,則傳回第一個參數的值,否則傳回第二個參數的值,但要求第二個參數的值也是非空或非0,否則傳回0。如果第一個參數是非空或非0時,不會計算第二個參數。
  • &

    如果兩個參數都非空且非0,則傳回第一個參數,否則傳回0。如果第一個參為0或為空,則不會計算第二個參數。
  • < <= = == != >= >

    比較兩端的參數,如果為true,則傳回1,否則傳回0。”==”是”=”的同義詞。”expr”首先嘗試将兩端參數轉換為整數,并做算術比較,如果轉換失敗,則按字元集排序規則做字元比較。
  • ()

    可以該表優先級,但需要用反斜杠轉義

示例:

a=3
b=4

echo `expr $a \> $b`  # 輸出0,>需要轉義
echo `expr $a '<' $b`  # 輸出1,也可以将特殊字元用引号引起來
echo `expr $a '>=' $b`  # 輸出0
echo `expr $a \<\= $b`  # 輸出1

c=0
d=5

echo `expr $c \& $d`  # 輸出0
echo `expr $a \& $b`  # 輸出3
echo `expr $c \| $d`  # 輸出5
echo `expr $a \| $b`  # 輸出3
           

read指令

read

指令用于從标準輸入中讀取單行資料。當讀到檔案結束符時,

exit code

為1,否則為0。

參數說明

  • -p

    : 後面可以接提示資訊
  • -t

    :後面跟秒數,定義輸入字元的等待時間,超過等待時間後會自動忽略此指令

執行個體:

[email protected]:~$ read name  # 讀入name的值
acwing yxc  # 标準輸入
[email protected]:~$ echo $name  # 輸出name的值
acwing yxc  #标準輸出
[email protected]:~$ read -p "Please input your name: " -t 30 name  # 讀入name的值,等待時間30秒
Please input your name: acwing yxc  # 标準輸入
[email protected]:~$ echo $name  # 輸出name的值
acwing yxc  # 标準輸出
           

echo指令

echo

用于輸出字元串。指令格式:

echo STRING
           

顯示普通字元串

echo "Hello AC Terminal"
echo Hello AC Terminal  # 引号可以省略
           

顯示轉義字元

echo "\"Hello AC Terminal\""  # 注意隻能使用雙引号,如果使用單引号,則不轉義
echo \"Hello AC Terminal\"  # 也可以省略雙引号
           

顯示變量

name=yxc
echo "My name is $name"  # 輸出 My name is yxc
           

顯示換行

echo -e "Hi\n"  # -e 開啟轉義
echo "acwing"
           

輸出結果:

Hi

acwing
           

顯示不換行

echo -e "Hi \c" # -e 開啟轉義 \c 不換行
echo "acwing"
           

輸出結果:

Hi acwing
           

顯示結果定向至檔案

原樣輸出字元串,不進行轉義或取變量(用單引号)

name=acwing
echo '$name\"'
           

輸出結果

$name\"
           

顯示指令的執行結果

輸出結果:

printf指令

printf

指令用于格式化輸出,類似于

C/C++

中的

printf

函數。

預設不會在字元串末尾添加換行符。

指令格式:

用法示例

腳本内容:

printf "%10d.\n" 123  # 占10位,右對齊
printf "%-10.2f.\n" 123.123321  # 占10位,保留2位小數,左對齊
printf "My name is %s\n" "yxc"  # 格式化輸出字元串
printf "%d * %d = %d\n"  2 3 `expr 2 \* 3` # 表達式的值作為參數
           

輸出結果:

123.
123.12    .
My name is yxc
2 * 3 = 6
           

test指令與判斷符号[]

邏輯運算符&&和||

  • &&

    表示與,

    ||

    表示或
  • 二者具有短路原則:

    expr1 && expr2

    :當

    expr1

    為假時,直接忽略

    expr2

    expr1 || expr2

    :當

    expr1

    為真時,直接忽略

    expr2

  • 表達式的

    exit code

    為0,表示真;為非零,表示假。(與

    C/C++

    中的定義相反)

test指令

在指令行中輸入

man test

,可以檢視

test

指令的用法。

test

指令用于判斷檔案類型,以及對變量做比較。

test

指令用

exit code

傳回結果,而不是使用

stdout

。0表示真,非0表示假。

例如:

test 2 -lt 3  # 為真,傳回值為0
echo $?  # 輸出上個指令的傳回值,輸出0
           
[email protected]:~$ ls  # 列出目前目錄下的所有檔案
homework  output.txt  test.sh  tmp
[email protected]:~$ test -e test.sh && echo "exist" || echo "Not exist"
exist  # test.sh 檔案存在
[email protected]:~$ test -e test2.sh && echo "exist" || echo "Not exist"
Not exist  # testh2.sh 檔案不存在
           

檔案類型判斷

指令格式:

測試參數 代表意義
-e 檔案是否存在
-f 是否為檔案
-d 是否為目錄

檔案權限判斷

指令格式:

測試參數 代表意義
-r 檔案是否可讀
-w 檔案是否可寫
-x 檔案是否可執行
-s 是否為非空檔案

整數間的比較

指令格式:

測試參數 代表意義
-eq a是否等于b
-ne a是否不等于b
-gt a是否大于b
-lt a是否小于b
-ge a是否大于等于b
-le a是否小于等于b

字元串比較

測試參數 代表意義
test -z STRING 判斷STRING是否為空,如果為空,則傳回true
test -n STRING 判斷STRING是否非空,如果非空,則傳回true(-n可以省略)
test str1 == str2 判斷str1是否等于str2
test str1 != str2 判斷str1是否不等于str2

多重條件判定

指令格式:

test -r filename -a -x filename
           
測試參數 代表意義
-a 兩條件是否同時成立
-o 兩條件是否至少一個成立
! 取反。如 test ! -x file,當file不可執行時,傳回true

判斷符号[]

[]

test

用法幾乎一模一樣,更常用于

if

語句中。另外

[[]]

[]

的加強版,支援的特性更多。

例如:

[ 2 -lt 3 ]  # 為真,傳回值為0
echo $?  # 輸出上個指令的傳回值,輸出0
           
[email protected]:~$ ls  # 列出目前目錄下的所有檔案
homework  output.txt  test.sh  tmp
[email protected]:~$ [ -e test.sh ] && echo "exist" || echo "Not exist"
exist  # test.sh 檔案存在
[email protected]:~$ [ -e test2.sh ] && echo "exist" || echo "Not exist"
Not exist  # testh2.sh 檔案不存在
           

注意:

  • []

    内的每一項都要用空格隔開
  • 中括号内的變量,最好用雙引号括起來
  • 中括号内的常數,最好用單或雙引号括起來

例如:

name="acwing yxc"
[ $name == "acwing yxc" ]  # 錯誤,等價于 [ acwing yxc == "acwing yxc" ],參數太多
[ "$name" == "acwing yxc" ]  # 正确
           

判斷語句

if…then形式

類似于

C/C++

中的

if-else

語句。

單層if

指令格式:

if condition
then
    語句1
    語句2
    ...
fi
           

示例:

a=3
b=4

if [ "$a" -lt "$b" ] && [ "$a" -gt 2 ]
then
    echo ${a}在範圍内
fi
           

輸出結果:

3在範圍内
           

單層if-else

指令格式

if condition
then
    語句1
    語句2
    ...
else
    語句1
    語句2
    ...
fi
           

示例:

a=3
b=4

if ! [ "$a" -lt "$b" ]
then
    echo ${a}不小于${b}
else
    echo ${a}小于${b}
fi
           

輸出結果:

3小于4
           

多層if-elif-elif-else

指令格式

if condition
then
    語句1
    語句2
    ...
elif condition
then
    語句1
    語句2
    ...
elif condition
then
    語句1
    語句2
else
    語句1
    語句2
    ...
fi
           

示例:

a=4

if [ $a -eq 1 ]
then
    echo ${a}等于1
elif [ $a -eq 2 ]
then
    echo ${a}等于2
elif [ $a -eq 3 ]
then
    echo ${a}等于3
else
    echo 其他
fi
           

輸出結果:

其他
           

case…esac形式

類似于

C/C++

中的

switch

語句。

指令格式

case $變量名稱 in
    值1)
        語句1
        語句2
        ...
        ;;  # 類似于C/C++中的break
    值2)
        語句1
        語句2
        ...
        ;;
    *)  # 類似于C/C++中的default
        語句1
        語句2
        ...
        ;;
esac
           

示例:

a=4

case $a in
    1)
        echo ${a}等于1
        ;;  
    2)
        echo ${a}等于2
        ;;  
    3)                                                
        echo ${a}等于3
        ;;  
    *)
        echo 其他
        ;;  
esac
           

輸出結果:

其他
           

循環語句

for…in…do…done

指令格式:

for var in val1 val2 val3
do
    語句1
    語句2
    ...
done
           

示例1,輸出a 2 cc,每個元素一行:

for i in a 2 cc
do
    echo $i
done
           

示例2,輸出目前路徑下的所有檔案名,每個檔案名一行:

for file in `ls`
do
    echo $file
done
           

示例3,輸出1-10

for i in $(seq 1 10)
do
    echo $i
done
           

示例4,使用{1…10} 或者 {a…z}

for i in {a..z}
do
    echo $i
done
           

for ((…;…;…)) do…done

指令格式:

for ((expression; condition; expression))
do
    語句1
    語句2
done
           

示例,輸出1-10,每個數占一行:

for ((i=1; i<=10; i++))
do
    echo $i
done
           

while…do…done循環

指令格式:

while condition
do
    語句1
    語句2
    ...
done
           

示例,檔案結束符為

Ctrl+d

,輸入檔案結束符後

read

指令傳回false。

while read name
do
    echo $name
done
           

until…do…done循環

當條件為真時結束。

指令格式:

until condition
do
    語句1
    語句2
    ...
done
           

示例,當使用者輸入

yes

或者

YES

時結束,否則一直等待讀入。

until [ "${word}" == "yes" ] || [ "${word}" == "YES" ]
do
    read -p "Please input yes/YES to stop this program: " word
done
           

break指令

跳出目前一層循環,注意與

C/C++

不同的是:

break

不能跳出case

句。

示例

while read name
do
    for ((i=1;i<=10;i++))
    do
        case $i in
            8)
                break
                ;;
            *)
                echo $i
                ;;
        esac
    done
done
           

該示例每讀入非EOF的字元串,會輸出一遍1-7。

該程式可以輸入Ctrl+d檔案結束符來結束,也可以直接用Ctrl+c殺掉該程序。

continue指令

跳出目前循環。

示例:

for ((i=1;i<=10;i++))
do
    if [ `expr $i % 2` -eq 0 ]
    then
        continue
    fi
    echo $i
done
           

該程式輸出1-10中的所有奇數。

死循環的處理方式

如果AC Terminal可以打開該程式,則輸入

Ctrl+c

即可。

否則可以直接關閉程序:

  1. 使用

    top

    指令找到程序的PID
  2. 輸入

    kill -9 PID

    即可關掉此程序

函數

bash

中的函數類似于

C/C++

中的函數,但

return

的傳回值與

C/C++

不同,傳回的是

exit code

,取值為0-255,0表示正常結束。

如果想擷取函數的輸出結果,可以通過

echo

輸出到

stdout

中,然後通過

$(function_name)

來擷取

stdout

中的結果。

函數的

return

值可以通過

$?

來擷取。

指令格式:

[function] func_name() {  # function關鍵字可以省略
    語句1
    語句2
    ...
}
           

不擷取

return

值和

stdout

示例

func() {
    name=yxc
    echo "Hello $name"
}

func
           

輸出結果:

Hello yxc
           

擷取 return值和stdout值

不寫

return

時,預設

return 0

示例

func() {
    name=yxc
    echo "Hello $name"

return 123

}

output=$(func)
ret=$?

echo "output = $output"
echo "return = $ret"
           

輸出結果:

output = Hello yxc
return = 123
           

函數的輸入參數

在函數内,

$1

表示第一個輸入參數,

$2

表示第二個輸入參數,依此類推。

注意:函數内的

$0

仍然是檔案名,而不是函數名。

示例:

func() {  # 遞歸計算 $1 + ($1 - 1) + ($1 - 2) + ... + 0
    word=""
    while [ "${word}" != 'y' ] && [ "${word}" != 'n' ]
    do
        read -p "要進入func($1)函數嗎?請輸入y/n:" word
    done

if [ "$word" == 'n' ]
then
    echo 0
    return 0
fi  

if [ $1 -le 0 ] 
then
    echo 0
    return 0
fi  

sum=$(func $(expr $1 - 1))
echo $(expr $sum + $1)

}

echo $(func 10)
           

輸出結果:

55
           

函數内的局部變量

可以在函數内定義局部變量,作用範圍僅在目前函數内。

可以在遞歸函數中定義局部變量。

指令格式:

local 變量名=變量值
           

例如:

#! /bin/bash

func() {
    local name=yxc
    echo $name
}
func

echo $name
           

輸出結果:

yxc

           

第一行為函數内的name變量,第二行為函數外調用name變量,會發現此時該變量不存在。

exit指令

exit

指令用來退出目前

shell

程序,并傳回一個退出狀态;使用

$?

可以接收這個退出狀态。

exit

指令可以接受一個整數值作為參數,代表退出狀态。如果不指定,預設狀态值是 0。

exit

退出狀态隻能是一個介于 0~255 之間的整數,其中隻有 0 表示成功,其它值都表示失敗。

示例:

建立腳本

test.sh

,内容如下:

#! /bin/bash

if [ $# -ne 1 ]  # 如果傳入參數個數等于1,則正常退出;否則非正常退出。
then
    echo "arguments not valid"
    exit 1
else
    echo "arguments valid"
    exit 0
fi
           

執行該腳本:

[email protected]:~$ chmod +x test.sh 
[email protected]:~$ ./test.sh acwing
arguments valid
[email protected]:~$ echo $?  # 傳入一個參數,則正常退出,exit code為0
0
[email protected]:~$ ./test.sh 
arguments not valid
[email protected]:~$ echo $?  # 傳入參數個數不是1,則非正常退出,exit code為1
1
           

檔案重定向

每個程序預設打開3個檔案描述符:

  • stdin

    标準輸入,從指令行讀取資料,檔案描述符為0
  • stdout

    标準輸出,向指令行輸出資料,檔案描述符為1
  • stderr

    标準錯誤輸出,向指令行輸出資料,檔案描述符為2

可以用檔案重定向将這三個檔案重定向到其他檔案中。

重定向指令清單

指令 說明

command > file

stdout

重定向到

file

command < file

stdin

重定向到

file

command >> file

stdout

以追加方式重定向到

file

command n> file

将檔案描述符

n

重定向到

file

command n>> file

将檔案描述符

n

以追加方式重定向到

file

輸入和輸出重定向

echo -e "Hello \c" > output.txt  # 将stdout重定向到output.txt中
echo "World" >> output.txt  # 将字元串追加到output.txt中

read str < output.txt  # 從output.txt中讀取字元串

echo $str  # 輸出結果:Hello World
           

同時重定向stdin和stdout

建立bash腳本:

#! /bin/bash

read a
read b

echo $(expr "$a" + "$b")
           

建立input.txt,裡面的内容為:

3
4
           

執行指令:

[email protected]:~$ chmod +x test.sh  # 添加可執行權限
[email protected]:~$ ./test.sh < input.txt > output.txt  # 從input.txt中讀取内容,将輸出寫入output.txt中
[email protected]:~$ cat output.txt  # 檢視output.txt中的内容
7
           

引入外部腳本

類似于

C/C++

中的

include

操作,

bash

也可以引入其他檔案中的代碼。

文法格式:

. filename  # 注意點和檔案名之間有一個空格

或

source filename
           

示例

建立

test1.sh

,内容為:

#! /bin/bash

name=yxc  # 定義變量name
           

然後建立

test2.sh

,内容為:

#! /bin/bash

source test1.sh # 或 . test1.sh

echo My name is: $name  # 可以使用test1.sh中的變量
           

執行指令:

[email protected]:~$ chmod +x test2.sh 
[email protected]:~$ ./test2.sh 
My name is: yxc
           

轉載自AcWing-Linux基礎課

繼續閱讀