Shell腳本快速入門學習

常用的Shell

Shell 既是一種腳本編程語言,也是一個連接內核和用戶的軟件。

常見的 Shell 有 sh、bash、csh、tcsh、ash 等。

bash shell 是 Linux 的默認 shell,bash 由 GNU 組織開發,保持了對 sh shell 的兼容性,是各種 Linux 發行版默認配置的 shell。

bash 兼容 sh 意味着,針對 sh 編寫的 Shell 代碼可以不加修改地在 bash 中運行。

儘管如此,bash 和 sh 還是有一些不同之處:

  • 一方面,bash 擴展了一些命令和參數;
  • 另一方面,bash 並不完全和 sh 兼容,它們有些行爲並不一致,但在大多數企業運維的情況下區別不大,特殊場景可以使用 bash 代替 sh。

查看Shell

Shell是一個程序,一般放在/bin/user/bin目錄下,Linux系統可用的Shell都可以使用下面命令查看

 

user@PxeCtrlSys:~$ cat /etc/shells 
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash

現在Linux基本都是用的bash Shell,查看Linux的默認shell

 

user@PxeCtrlSys:~$ echo $SHELL
/bin/bash

運行方法

  • 作爲可執行程序

代碼保存test.sh文本中

 

# 進入腳本所在目錄,讓腳本擁有可執行權限
chmod +x ./test.sh
# 執行方式
./test.sh

一定要寫成./test.sh,而不是test.sh,運行其它二進制的程序也一樣,直接寫 test.sh,linux 系統會去 PATH 裏尋找有沒有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin/usr/sbin 等在 PATH 裏,你的當前目錄通常不在 PATH 裏,所以寫成 test.sh 是會找不到命令的,要用 ./test.sh 告訴系統說,就在當前目錄找。

  • 作爲解釋器參數

直接運行解釋器,其參數就是 shell 腳本的文件名

 

/bin/sh test.sh
# 或者
/bin/bash test.sh

Shell變量

在Bash Shell中,每一個變量的值都是字符串,無論給變量賦值時有沒使用引號,值都會以字符串的形式存儲,即使將整數或小數賦值給變量,他們也會被視爲字符串。

變量賦值

定義變量,三種定義方式,顯式賦值

 

name="test"
name='test'
name=test

注意,變量名和等號之間不能有空格,這可能和你熟悉的所有編程語言都不一樣。同時,變量名的命名須遵循如下規則:

  • 命名只能使用英文字母,數字和下劃線,首個字符不能以數字開頭。
  • 中間不能有空格,可以使用下劃線(_)。
  • 不能使用標點符號。
  • 不能使用bash裏的關鍵字(可用help命令查看保留關鍵字)。

如果變量值包含空白字符,就必須使用引號包圍起來,例如name="bom smith"

用語句給變量賦值

 

for file in `ls /etc`
或
for file in $(ls /etc)

/etc目錄下的文件名循環出來。

使用變量

 

name="test"
echo $name
# 或者加花括號,含義一樣,加花括號是爲了幫助解釋器識別變量的邊界
echo ${name}

例如和變量連接的字符

 

#!/bin/bash
for item in Python Java Shell Bat; do
    echo "This is ${item}Script"
done

運行結果如下

 

This is PythonScript
This is JavaScript
This is ShellScript
This is BatScript

如果沒有花括號,那麼變量就是$itemScript,其值爲空,結果就會錯誤。推薦加花括號{}

修改變量的值

已定義過的變量可以重新賦值

 

#!/bin/bash
name='test'
echo ${name}  # test
name="new"
echo $name  # new

變量賦值不能加$符號,只有在使用鍵盤時才能加$

命令執行結果賦值給變量

支持將命令的執行結果賦值給變量使用,常見的兩種方式:

 

value=`command`
# 或
value=$(command)

# 默認輸出以空白符填充
echo $value
# 按照原格式輸出,例如有換行符的
echo "$value"

第一種是使用反引號包圍起來,第二種是使用$()包圍起來(推薦使用第二種方式)。command是執行命令。

 

user@PxeCtrlSys:~$ res=`who`
user@PxeCtrlSys:~$ echo $res
user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16)
user@PxeCtrlSys:~$ res=$(ls /)
user@PxeCtrlSys:~$ echo $res
bin boot dev etc home initrd.img lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz

執行結果賦值給變量,輸出不會自動添加換行符。

如果被替換的命令的輸出內容包括多行(也即有換行符),或者含有多個連續的空白符,那麼在輸出變量時應該將變量用雙引號包圍,否則系統會使用默認的空白符來填充,這會導致換行無效,以及連續的空白符被壓縮成一個。請看下面的代碼:

 

user@PxeCtrlSys:~$ res=`who`
# 不用引號包圍變量,輸出不會自動換行
user@PxeCtrlSys:~$ echo $res
user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16)
# 使用引號包圍變量,輸出按照原來的格式
user@PxeCtrlSys:~$ echo "$res"
user     pts/0        2019-03-11 09:04 (192.168.96.16)
user     pts/1        2019-03-11 09:04 (192.168.96.16)

爲了防止出現格式混亂的情況,建議在輸出變量時加上雙引號。

只讀變量

使用 readonly 命令可以將變量定義爲只讀變量,只讀變量的值不能被改變。
下面的例子嘗試更改只讀變量,結果報錯:

 

#!/bin/bash
name='test'
readonly name
name='new'
echo $name

運行結果

 

test
/tmp/280278210/main.sh: line 4: name: readonly variable
# 當改爲只讀後,再次賦值就會報錯

刪除變量

使用 unset 命令可以刪除變量

 

#!/bin/bash
name='test'
unset name
echo $name  # 執行沒有輸出

變量刪除後不能再次使用。unset命令不能刪除只讀變量。

變量類型,作用域

運行shell時,會同時存在三種變量:

  1. 全局變量 指變量在當前的整個 Shell 會話中都有效。每個 Shell 會話都有自己的作用域,彼此之間互不影響。在 Shell 中定義的變量,默認就是全局變量。
  2. 局部變量 在 Shell 函數中定義的變量默認也是全局變量,它和在函數外部定義變量擁有一樣的效果;要想變量的作用域僅限於函數內部,那麼可以在定義時加上local命令,此時該變量就成了局部變量。
  3. 環境變量 只在當前 Shell 會話中有效,如果使用export命令將它導出,那麼它就在所有的子 Shell 中也有效了。

Shell字符串

字符串最常用的數據類型,可以用單/雙引號,也可以不用引號。

單引號

 

str='this is a string'

單引號字符串的限制:

  • 單引號裏面的任何字符都會原樣輸出,單引號字符串裏面的變量是無效的,也就是變量那一串就只是字符串;
  • 單引號字符中不能出現單獨一個的單引號(對單引號使用轉義符後也不行),但能成對出現,相當於字符串拼接s='abc\'0e',這個就會報錯,而s='abc\'0'e'會輸出abc\0e

雙引號

  • 雙引號中可以有變量,輸出時會先解析裏面的變量和命令
  • 雙引號也可以出現轉義字符

 

#!/bin/bash
name='lr'
echo $name
s1="my name is $name"
echo $s1
s2="my name is "$name""
echo $s2
s3="my name is \"$name\""
echo $s3

# 運行結果
lr
my name is lr
my name is lr
my name is "lr"

單/雙引號使用場景

建議:

  • 如果變量的值是數字,那麼不加引號a=1
  • 如果要將字符串原樣輸出,就用反引號str='單引號中原樣輸出${test}'
  • 沒有特別要求最好都是用雙引號(最常見)。

拼接字符串

字符串的拼接(也稱字符串連接或者字符串合併),在 Shell 中你不需要使用任何運算符,將兩個字符串並排放在一起就能實現拼接。

 

#!/bin/bash

name='lr'

s1="hello, $name"  # 雙引號中可以直接使用變量
echo $s1
s2="hello, "$name""  # 相當於兩對雙引號拼接
echo $s2
s3='hello, $name'  # 單引號中變量都當作普通字符串
echo $s3
s4='hello, "$name"'
echo $s4
s5='hello, '$name''  # 相當於兩對單引號和變量進行拼接
echo $s5

nick='xy'
s6=$nick$name  # 中間不能有空格
s7="$nick $name"  # 如果是雙引號包圍,就允許中間有空格
s8=$nick"..."$name  # 中間可以出現其他字符串
s9="$nick...$name"  # 中間也可以這樣寫
s10="暱稱${nick}...${name}名字"

echo $s6 
echo $s7 
echo $s8 
echo $s9 
echo $s10


# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
hello, lr
hello, lr
hello, $name
hello, "$name"
hello, lr

xylr
xy lr
xy...lr
xy...lr
暱稱xy...lr名字

獲取字符串長度

使用${#變量名}獲取長度

 

#!/bin/bash
name='lr'
echo ${#name}

# 運行結果
2

截取字符串

Shell 截取字符串通常有兩種方式:從指定位置開始截取和從指定字符(子字符串)開始截取。

從指定位置開始截取

種方式需要兩個參數:除了指定起始位置,還需要截取長度,才能最終確定要截取的字符串。

從字符串左邊開始計數,格式爲${string: start :length}

 

#!/bin/bash
str="I love Python, but I need study Shell"
echo ${str:2:7}  # 下標以0開始,從第2個開始截取,共截取7個字符
echo ${str:19}  # 省略長度,直接從指定位置截取到最後


# 運行結果
love Py
I need study Shell

從右邊開始計數,格式爲${string: 0-start :length}

多了0-,這是固定的寫法,專門用來表示從字符串右邊開始計數。

  • 從左邊開始計數時,起始數字是 0(這符合程序員思維);從右邊開始計數時,起始數字是 1(這符合常人思維)。計數方向不同,起始數字也不同。
  • 不管從哪邊開始計數,截取方向都是從左到右。

 

echo ${str:0-5:2}  # 從右向左數第5個,長度爲2的字符串
echo ${str:0-5}  # 省略長度,直接從指定位置截取到最後


# 運行結果
Sh
Shell

從指定字符(子字符串)開始截取

這種截取方式無法指定字符串長度,只能從指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右邊的所有字符,也可以截取左邊的所有字符。

使用 # 號截取右邊字符,格式爲${string#*chars}

其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一種,表示任意長度的字符串。*chars連起來使用的意思是:忽略左邊的所有字符,直到遇見 charschars 不會被截取)。

 

#!/bin/bash

str="I love Python, but I need study Shell"
echo ${str#*,}  # 以 , 分割,取右邊

echo ${str#*Python,}  # 以Python , 分割取右邊

echo ${str#I love}  # 如果不需要忽略左邊的字符,那麼也可以不寫*

echo ${str#*o}  # 以o分割,因爲字符串有好兩個o,那麼遇到第一個就結束了

echo ${str##*o}  # 使用兩個#,就可以匹配到最後一個指定字符(子字符串)右方內容

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
but I need study Shell
but I need study Shell
Python, but I need study Shell
ve Python, but I need study Shell
n, but I need study Shell

如果希望直到最後一個指定字符(子字符串)再匹配結束,那麼可以使用##,具體格式爲:${string##*chars}

使用 % 截取左邊字符,格式爲${string%chars*}

 

#!/bin/bash

str="I love Python, but I need study Shell"
echo ${str%,*}  # 以,分割,取左邊

echo ${str%Python,*}  # 以Python,分割取左邊

echo ${str%Shell}  # 如果不需要忽略右邊的字符,那麼也可以不寫*

echo ${str%o*}  # 以o分割,因爲字符串有好兩個o,從右往左,那麼遇到第一個就結束了

echo ${str%%o*}  # 使用兩個%%,就可以匹配到從右往左的最後一個指定字符(子字符串)左方內容


# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
I love Python
I love
I love Python, but I need study
I love Pyth
I l

截取字符串彙總

格式 說明
${string: start :length} string 字符串的左邊索引爲 start 的字符開始,向右截取 length 個字符。
${string: start} string 字符串的左邊索引爲 start 的字符開始截取,直到最後。
${string: 0-start :length} string 字符串的右邊第 start 個字符開始,向右截取 length 個字符。
${string: 0-start} string 字符串的右邊第 start 個字符開始截取,直到最後。
${string#*chars} 從 string 字符串第一次出現 *chars 的位置開始,截取 *chars 右邊的所有字符。
${string##*chars} 從 string 字符串最後一次出現 *chars 的位置開始,截取 *chars 右邊的所有字符。
${string%*chars} 從 string 字符串第一次出現 *chars 的位置開始,截取 *chars 左邊的所有字符。
${string%%*chars} string 字符串最後一次出現 *chars 的位置開始,截取 *chars 左邊的所有字符。

查找字符串

 

#!/bin/bash
str="I love Python, but I need study Shell"
echo `expr index "$str" SP`  # 查找字符S或者P最先出現的位置,下標以1開始,第8個字符爲P,就輸入了8

# 運行結果
8

以上腳本中 ` 是反引號,而不是單引號 '

Shell數組

bash支持一維數組(但不支持多維數組),並且沒有限制數組大小。數組元素的下標從0開始,獲取數組元素使用小標,下標可以試整數或算術表達式,其值應大於等於0。

定義數組

用括號表示數組,數組元素用空格符號分隔開。

 

數組名=(值1 值2 值3 ... 值n)

或者

 

array_name=(
value0
value1
...
valuen
)

還可以單獨定義數組的各個分量

 

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

可以不使用連續的下標,而且下標的範圍沒有限制

 

array=([2]=12 [5]=99)

echo ${array[0]}  # 爲空值
echo ${array[2]}  # 輸出值爲12

array[0]=5  # 賦值一個
echo ${array[0]}  # 輸出值爲5

# 獲取數組的所有元素
echo ${array[*]}
5 12 99
# 獲取數組長度
echo ${#array[*]}
3

讀取數組

一般格式

 

${數組名[下標]}

例如valuen=${array_name[n]},輸出數組所有元素用echo ${array_name[@]}

 

#!/bin/bash
arr[0]=1
arr[2]=2
echo ${arr[@]}  # 使用 @ 符號可以獲取數組中的所有元素:  1 2
echo ${arr[2]}  # 2
echo ${arr[1]}  # 沒有的值獲取爲空

使用@*可以獲取數組中的所有元素。

獲取數組的長度

所謂數組長度,就是數組元素的個數。

利用@*,可以將數組擴展成列表,然後使用#來獲取數組元素的個數,格式爲 ${#array_name[@]}${#array_name[*]} ,其中array_name 表示數組名。兩種形式是等價的。

 

#!/bin/bash
arr=(1 2 3 "abc")
echo ${#arr[@]}  # 獲取數組的長度:4
echo ${#arr[*]}  # *也可以:4
echo ${#arr[3]}  # 獲取數組中單獨元素的長度:3

獲取數組中元素爲字符串的長度${#arr[2]}(假設下標爲2的元素爲字符串),因爲獲取字符串長度的方法是 ${#string_name}

刪除數組的元素或數組

使用 unset arr[index] 刪除數組元素,使用 unset arr 刪除整個數組,所有元素都會消失。

 

#!/bin/bash

arr=(1 2 3 "abc")
echo ${#arr[@]}  # 獲取數組的長度:4
echo 原來數組的長度爲:${#arr[*]}  # *也可以:4
echo ${#arr[3]}  # 獲取數組中單獨元素的長度:3

unset arr[1]
echo "刪除第二個元素後長度爲:${#arr[*]}"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh
4
原來數組的長度爲:4
3
刪除第二個元素後長度爲:3

數組拼接、合併

將兩個數組連接成一個數組。

拼接數組的思路是:先利用@或*,將數組擴展成列表,然後再合併到一起。格式爲 array_new=(${array1[@]} ${array2[@]})array_new=(${array1[*]} ${array2[*]})

 

#!/bin/bash

arr1=(1 2 3 "abc")
arr2=(66)

echo "數組1:${arr1[*]}"
echo "數組2:${arr2[@]}"

new_arr=(${arr1[*]} ${arr2[@]})

echo "合併後的數組爲:${new_arr[*]}"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
數組1:1 2 3 abc
數組2:66
合併後的數組爲:1 2 3 abc 66

Shell註釋

#開始的行就是註釋,會被解釋器忽略

通過每一行添加一個#來設置多行註釋

如果在開發過程中,遇到大段的代碼需要臨時註釋起來,過一會兒又取消註釋,怎麼辦呢?

每一行加個#符號太費力了,可以把這一段要註釋的代碼用一對花括號括起來,定義成一個函數,沒有地方調用這個函數,這塊代碼就不會執行,達到了和註釋一樣的效果。

多行註釋

 

:<<EOF
註釋內容...
註釋內容...
註釋內容...
EOF

EOF也可以使用其他符號

 

:<<'
註釋內容...
註釋內容...
註釋內容...
'

:<<!
註釋內容...
註釋內容...
註釋內容...
!

Shell腳本傳遞參數

想腳本傳遞參數,腳本內獲取參數的格式爲:$nn代表一個數字,1爲執行腳本的第一個參數,2爲執行腳本的第二個參數,以此類推...

給腳本文件傳遞參數

向腳本傳遞3個參數,其中$0表示執行的文件名

 

user@PxeCtrlSys:~$ vim test.sh
user@PxeCtrlSys:~$ chmod +x test.sh  # 添加執行權限
user@PxeCtrlSys:~$ ./test.sh 
執行的文件名:./test.sh
傳遞的第一個參數:
傳遞的第二個參數:
傳遞的第三個參數:
所有參數:

user@PxeCtrlSys:~$ ./test.sh  1 2
執行的文件名:./test.sh
傳遞的第一個參數:1
傳遞的第二個參數:2
傳遞的第三個參數:
所有參數:1 2

user@PxeCtrlSys:~$ ./test.sh  1 2 3
執行的文件名:./test.sh
傳遞的第一個參數:1
傳遞的第二個參數:2
傳遞的第三個參數:3
所有參數:1 2 3

 

# test.sh文件內容
#!/bin/bash

echo "執行的文件名:$0"
echo "傳遞的第一個參數:$1"
echo "傳遞的第二個參數:$2"
echo "傳遞的第三個參數:$3"
echo "所有參數:$*"

如果參數個數太多,達到或者超過了 10 個,那麼就得用${n}的形式來接收了,例如 ${10}${23}{ }的作用是爲了幫助解釋器識別參數的邊界,這跟使用變量時加{ }是一樣的效果。

特殊字符處理參數(特殊變量)

參數處理 說明
$# 獲取傳遞到腳本的參數個數
$* 以一個單字符串顯示所有向腳本傳遞的參數,如$*就和"$1 $2 $3 ... $n"輸出形式一樣
$$ 腳本運行的當前ID號
$! 後臺運行的最後一個進程的ID號
$@ $*相同,但是使用時加括號,斌仔引號中返回每個參數,"$1" "$2" … "$n"形式
$- 顯示Shell使用的當前選項,與set命令功能相同
$? 顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表示有錯誤

@ 的區別

  • 相同點:都是引用所有參數
  • 不同點:只有在雙引號中提現出來。假設在腳本運行時寫了三個參數1、2、3,則*等價於"1 2 3"(傳遞了一個參數:會將所有的參數從整體上看做一份數據,而不是把每個參數都看做一份數據。),而@等價於"1" "2" "3"(仍然將每個參數都看作一份數據,彼此之間是獨立的)

 

#!/bin/bash

echo "執行的文件名:$0"
echo "傳遞的第一個參數:$1"
echo "傳遞的第二個參數:$2"
echo "傳遞的第三個參數:$3"

echo "\$*演示"
for i in "$*";do
    echo $i
done

echo "\$@演示"
for j in $@;do
    echo $j
done

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 1 2 3
執行的文件名:./test.sh
傳遞的第一個參數:1
傳遞的第二個參數:2
傳遞的第三個參數:3
$*演示  # 只循環了一次,因爲$*當作一個參數
1 2 3
$@演示  # 循環多次,$@當作多個參數
1
2
3

Shell函數

shell函數格式

 

[ function ] funname [()]

{

    action;

    [return int;]

}
  • 可以帶function func()定義,也可以直接func()定義,不帶任何參數。
  • 參數返回,可以顯示加:return返回,如果不加,將以最後一條命令運行結果,作爲返回值。return後跟數值n(0-255)

無返回值函數

 

#!/bin/bash

func(){
    echo "這是一個函數中的內容" 
}

echo "開始調用函數"
func
echo "調用函數完成"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
開始調用函數
這是一個函數中的內容
調用函數完成

有返回值函數

定義一個帶return的函數

 

#!/bin/bash

func(){
    echo "請輸入兩個數,執行加法"
    echo -n "請輸入第一個數:"
    read num1
    echo -n "請輸入第二個數:"
    read num2
    # return $[ $num1 + $num2]
    # 下方表達式也正確
    return $(( $num1 + $num2 ))
}

echo "開始調用函數"
func
echo "函數返回值爲 $?"
echo "調用函數完成"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
開始調用函數
請輸入兩個數,執行加法
請輸入第一個數:2
請輸入第二個數:3
函數返回值爲 5
調用函數完成

函數返回值在調用該函數後通過 $? 來獲得。

所有函數在使用前必須定義。這就是說必須將函數放在腳本開始部分,直到shell解釋器首次發現它時纔可以使用。調用函數僅使用其函數名即可。

$? 使用方法:返回值或退出狀態

$?用來獲取函數返回值或者上一個命令的退出狀態

每一條 Shell 命令,不管是 Bash 內置命令(例如 testecho),還是外部的 Linux 命令(例如 cdls),還是自定義的 Shell 函數,當它退出(運行結束)時,都會返回一個比較小的整數值給調用(使用)它的程序,這就是命令的退出狀態(exit status)。

if 語句的判斷條件,從本質上講,判斷的就是命令的退出狀態。

$? 獲取上一次命令退出狀態

退出狀態,就是上一個命令執行後的返回結果。退出狀態是一個數字,一般情況下,大部分命令執行成功會返回 0,失敗返回 1,這和C語言的 main() 函數是類似的。

不過,也有一些命令返回其他值,表示不同類型的錯誤。

 

#!/bin/bash

echo -n 請輸入a:
read a
echo -n 請輸入b:
read b

(( $a == $b ));
echo "退出狀態:$?"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
請輸入a:1
請輸入b:2
退出狀態:1
user@PxeCtrlSys:~$ echo $?
0
user@PxeCtrlSys:~$ ./test.sh 
請輸入a:6
請輸入b:6
退出狀態:0
user@PxeCtrlSys:~$ echo $?
0

$? 獲取函數的返回值

給函數傳遞參數

Shell調用函數也可以向其傳遞參數。在函數體內部,通過 $n 的形式來傳遞參數。例如$1表示第一個參數。$2表示第二次參數

 

#!/bin/bash

func(){
    echo "第一個參數:$1"
    echo "第二個參數:$2"
    echo "第二個參數:${2}"
    echo "第五個參數:$5"
    echo "第10個參數:$10"  # 這個相當於第一個參數$1連接一個0
    echo "第10個參數:${10}"
    echo "第11個參數:$11"  # 相當於第一個參數$1連接一個1
    echo "第11個參數:${11}"
    echo "參數總數有 $# 個"
    echo "作爲一個地府傳輸出所有參數:$*"
}

echo "開始調用函數"
func 0 2 3 4 5 23 36 65 99 123
echo "函數返回值爲 $?"
echo "調用函數完成"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
開始調用函數
第一個參數:0
第二個參數:2
第二個參數:2
第五個參數:5
第10個參數:00
第10個參數:123
第11個參數:01
第11個參數:
參數總數有 10 個
作爲一個地府傳輸出所有參數:0 2 3 4 5 23 36 65 99 123
函數返回值爲 0
調用函數完成

$10不能獲取到第10個參數,正確用法是${10}n>=10時,需要使用${n}來獲取參數

處理參數的特殊字符

參數處理 說明
$# 傳遞到腳本的參數個數
$* 以一個單字符串顯示所有向腳本傳遞的參數
$$ 腳本運行的當前進程ID號
$! 後臺運行的最後一個進程的ID號
$@ 與$*相同,但是使用時加引號,並在引號中返回每個參數。
$- 顯示Shell使用的當前選項,與set命令功能相同。
$? 顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。

Shell基本運算符

支持的運算符

  • 算術運算符
  • 關係運算符
  • 布爾運算符
  • 字符串運算符
  • 文件測試運算符

原生bash不支持簡單的數學運算,但可以使用其他命令實現,例如awkexpr(最常用)

expr是一款表達式計算工具,使用它完成表達式的求值操作

例如,兩個數量價,注意是反引號,而不是單引號

 

#!/bin/bash


val=`expr 2+2`
echo "求值結果:$val"
val=`expr 2 + 2`
echo "求值結果:$val"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
求值結果:2+2
求值結果:4
  • 表達式和運算符之間要用空格,例如2+2是不正確的,需要寫成2 + 2,這與大多數編程語言不同
  • 完整的表達式要被兩個反引號` `包含.

算術運算符

兩個數直接加不會進行算術運算

Shell 和其它編程語言不同,Shell 不能直接進行算數運算,必須使用數學計算命令。

默認情況下,Shell 不會直接進行算術運算,而是把+兩邊的數據(數值或者變量)當做字符串,把+當做字符串連接符,最終的結果是把兩個字符串拼接在一起形成一個新的字符串。這是因爲,在Shell如果不特別知名,每一個變量都是字符串,無論賦值的時候有沒使用引號,值都會以字符串形式存儲,默認情況下不區分變量類型。

Shell expr:進行整數計算

 

a 不等於 b
#!/bin/bash

a=2
b=3

echo `expr $a + $b`
echo `expr $a - $b`
echo `expr $a \* $b`  # 做乘法需要添加斜槓轉義
echo `expr $b / $a`
echo `expr $b % $a`

c=$b
echo "賦值後c的值:$c"

if [ $a == $b ]
then
    echo "a 等於 b"
else
    echo "a 不等於 b"
fi

if [ $b == $c ]
then
    echo "b 等於 c"
fi

if [ $a != $b ]
then
    echo "a 不等於 b"
fi

if [ $c != $b ]
then
    echo "c 不等於 b"
fi

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
5
-1
6
1
1
賦值後c的值:3
a 不等於 b
b 等於 c
a 不等於 b
運算符 說明 (例如a=2、b=3) 舉例
+ 加法 `expr b` 結果爲 30。
- 減法 `expr b` 結果爲 -10。
* 乘法 `expr b` 結果爲 200。
/ 除法 `expr a` 結果爲 2。
% 取餘 `expr a` 結果爲 0。
= 賦值 a=$b 將把變量 b 的值賦給 a。
== 相等。用於比較兩個數字,相同則返回 true。 [ b ] 返回 false。
!= 不相等。用於比較兩個數字,不相同則返回 true。 [ b ] 返回 true。
  • 條件表達式要放在方括號之間,並且要有空格,例如: [$a==$b] 是錯誤的,必須寫成 [ $a == $b ]
  • 乘號(*)前邊必須加反斜槓(\*)才能實現乘法運算;
  • if...then...fi 是條件語句
  • 在 MAC 中 shell 的 expr 語法是:$((表達式)),此處表達式中的 "*" 不需要轉義符號 ""

數學計算命令

Shell 中常用的六種數學計算方式

運算操作符/運算命令 說明
(()) 用於整數運算,效率很高,推薦使用
let 用於整數運算,和 (()) 類似。
[$[] 用於整數運算,不如 (()) 靈活。
expr 可用於整數運算,也可以處理字符串。比較麻煩,需要注意各種細節,不推薦使用。
bc Linux下的一個計算器程序,可以處理整數和小數。Shell 本身只支持整數運算,想計算小數就得使用 bc 這個外部的計算器。
declare -i 將變量定義爲整數,然後再進行數學運算時就不會被當做字符串了。功能有限,僅支持最基本的數學運算(加減乘除和取餘),不支持邏輯運算、自增自減等,所以在實際開發中很少使用。

Shell (()):對整數進行數學運算

(( )) 只能進行整數運算,不能對小數(浮點數)或者字符串進行運算。語法格式 ((表達式)) ,就是將數學運算表達式放在(())之間。

表達式可以只有一個,也可以有多個,多個表達式之間以逗號,分隔。對於多個表達式的情況,以最後一個表達式的值作爲整個 (( )) 命令的執行結果。

可以使用$獲取 (( )) 命令的結果,這和使用$獲得變量值是類似的。

運算操作符/運算命令 說明
((a=10+66) ((b=a-15)) ((c=a+b)) 這種寫法可以在計算完成後給變量賦值。以 ((b=a-15)) 爲例,即將 a-15 的運算結果賦值給變量 c。 注意,使用變量時不用加$前綴,(( )) 會自動解析變量名
a=$((10+66) b=$((a-15)) c=$((a+b)) 可以在 (( )) 前面加上$符號獲取 (( )) 命令的執行結果,也即獲取整個表達式的值。以 c=$((a+b)) 爲例,即將 a+b 這個表達式的運算結果賦值給變量 c。 注意,類似 c=((a+b)) 這樣的寫法是錯誤的,不加$就不能取得表達式的結果。
((a>7 && b==c)) (( )) 也可以進行邏輯運算,在 if 語句中常會使用邏輯運算。
echo $((a+10)) 需要立即輸出表達式的運算結果時,可以在 (( )) 前面加$符號。
((a=3+5, b=a+10)) 對多個表達式同時進行計算。

(( )) 中使用變量無需加上$前綴,(( )) 會自動解析變量名,這使得代碼更加簡潔,也符合程序員的書寫習慣。

算術運算

 

# 直接輸出運算結果
user@PxeCtrlSys:~$ echo $((1+1))
2
user@PxeCtrlSys:~$ echo $((2*3))
6
# 計算完成後,給變量賦值
user@PxeCtrlSys:~$ i=5
user@PxeCtrlSys:~$ ((i=i*2))
user@PxeCtrlSys:~$ echo $i
10
user@PxeCtrlSys:~$ ((i*=2))  # 簡寫,等效於 ((i=i*2))
user@PxeCtrlSys:~$ echo $i
20
# 複雜運算,結果賦值給變量a,變量在括號內
user@PxeCtrlSys:~$ ((a=2-5*2/4+2**2))
user@PxeCtrlSys:~$ echo $a
4
# 運算結果賦值給變量b,變量b在括號外,需要使用$
user@PxeCtrlSys:~$ b=$((2-5*2/4+2**2))
user@PxeCtrlSys:~$ echo $b
4
# 直接輸出表達式的值,$符號不能去掉
user@PxeCtrlSys:~$ echo $((2-5*2/4+2**2))
4
# 利用公式計算1-100的和
user@PxeCtrlSys:~$ echo $((100*(100+1)/2))
5050

邏輯運算

 

# 結果爲真,輸出1,1表示真
user@PxeCtrlSys:~$ echo $((3<5))
1
user@PxeCtrlSys:~$ echo $((3>5))
0
user@PxeCtrlSys:~$ echo $((3==2+1))
1
# 多條件成立
user@PxeCtrlSys:~$ if ((8>6&&5==2+3))
> then
> echo yes
> fi
yes

(())進行自增++和自減--運算

 

user@PxeCtrlSys:~$ a=10
# ++在後面,先輸出a的值,在自增
user@PxeCtrlSys:~$ echo $((a++))
10
user@PxeCtrlSys:~$ echo $a
11
# --在後面,先輸出a的值,再自減
user@PxeCtrlSys:~$ echo $((a--))
11
user@PxeCtrlSys:~$ echo $a
10
# --在前面,先自減,再輸出a的值
user@PxeCtrlSys:~$ echo $((--a))
9
user@PxeCtrlSys:~$ echo $a
9
# ++在前面,先自增,再輸出a的值
user@PxeCtrlSys:~$ echo $((++a))
10
user@PxeCtrlSys:~$ echo $a
10

多個表達式計算

 

# 先計算第一個表達式,再計算第二個表達式
user@PxeCtrlSys:~$ ((a=3*2, b=a+6))
user@PxeCtrlSys:~$ echo $a $b
6 12
# 以最後一個表達式的結果作爲整個(())命令的執行結果
user@PxeCtrlSys:~$ c=$((1+2, a+b))
user@PxeCtrlSys:~$ echo $c
18

Shell let:對整數進行數學運算

和雙小括號 (( )) 一樣,let 命令也只能進行整數運算,不能對小數(浮點數)或者字符串進行運算。

語法格式 let 表達式let "表達式"let '表達式',都等價於 ((表達式))

當表達式中含有 Shell 特殊字符(例如 |)時,需要用雙引號" "或者單引號' '將表達式包圍起來。

(( )) 類似,let 命令也支持一次性計算多個表達式,並且以最後一個表達式的值作爲整個 let 命令的執行結果。但是,對於多個表達式之間的分隔符,let(( )) 是有區別的:

  • let 命令以空格來分隔多個表達式;
  • (( )) 以逗號,來分隔多個表達式。

 

user@PxeCtrlSys:~$ i=2
user@PxeCtrlSys:~$ let i+=3
user@PxeCtrlSys:~$ echo $i
5

let i+=3等價於((i+=3)),但後者效率更高。

let後面可以跟多個表達式,用空格分隔

 

user@PxeCtrlSys:~$ a=3
user@PxeCtrlSys:~$ let b=3**2 c=a+b
user@PxeCtrlSys:~$ echo $a $b
3 9
user@PxeCtrlSys:~$ echo $c
12

Shell $[]:對整數進行數學運算

(())let 命令類似,$[] 也只能進行整數運算。語法爲 $[表達式]

$[] 會對表達式進行計算,並取得計算結果。如果表達式中包含了變量,那麼你可以加$,也可以不加。

 

# 直接輸出結果
user@PxeCtrlSys:~$ echo $[2*3]
6
user@PxeCtrlSys:~$ echo $[(2+3)/3]
1
user@PxeCtrlSys:~$ echo $[(2+3)%3]
2
user@PxeCtrlSys:~$ a=6
# 將結果賦值給變量
user@PxeCtrlSys:~$ b=$[a*2]
user@PxeCtrlSys:~$ echo $b
12
user@PxeCtrlSys:~$ echo $[a+b]
18
# 變量前加$對結果沒有影響
user@PxeCtrlSys:~$ echo $[$a+$b]
18

Shell declare -i:將變量聲明爲整數

默認情況下,Shell每一個變量的值都是一個字符串,即使給變量賦值一個數字,它也是字符串。

使用 declare 命令的-i選項可以將一個變量聲明爲整數類型,這樣在進行數學計算時就不會作爲字符串處理了。

 

#!/bin/bash

echo 定義之前,直接求兩個數的和
m=2
n=3
echo $m+$n
echo 求和後賦值給一個變量
ret=$m+$n
echo $ret

echo -e "\n聲明變量爲整數"
declare -i m n ret
m=2
n=3
echo 直接輸出聲明後的求和
echo $m+$n

ret=$m+$n
echo 求和後賦值變量
echo $ret


# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
定義之前,直接求兩個數的和
2+3
求和後賦值給一個變量
2+3

聲明變量爲整數
直接輸出聲明後的求和
2+3
求和後賦值變量
5

除了將 mn 定義爲整數,還必須將 ret 定義爲整數,如果不這樣做,在執行ret=$m+$n時,Shell 依然會將 mn 視爲字符串。

此外,也不能寫類似echo $m+$n這樣的語句,這種情況下 mn 也會被視爲字符串。

總之,除了將參與運算的變量定義爲整數,還得將承載結果的變量定義爲整數,而且只能用整數類型的變量來承載運算結果,不能直接使用 echo 輸出。

(())let$[] 不同,declare -i的功能非常有限,僅支持最基本的數學運算(加減乘除和取餘),不支持邏輯運算(比較運算、與運算、或運算、非運算),所以在實際開發中很少使用。

關係運算符

關係運算符只支持數字,不支持字符串,除非字符串的值是數字

運算符 說明(例如a=2、b=3) 舉例
-eq 等於檢測兩個數是否相等,相等返回 true。 [ b ] 返回 false。
-ne 不等於檢測兩個數是否不相等,不相等返回 true。 [ b ] 返回 true。
-gt 大於檢測左邊的數是否大於右邊的,如果是,則返回 true。 [ b ] 返回 false。
-lt 小於檢測左邊的數是否小於右邊的,如果是,則返回 true。 [ b ] 返回 true。
-ge 大等於檢測左邊的數是否大於等於右邊的,如果是,則返回 true。 [ b ] 返回 false。
-le 小等於檢測左邊的數是否小於等於右邊的,如果是,則返回 true。 [ b ] 返回 true。

 

#!/bin/bash

a=2
b=3

if [ $a -eq $b ]
then
    echo "a 大於 b"
else
    echo "a 小於 b"
fi


# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
a 小於 b

布爾運算符

運算符 說明(例如a=2、b=3) 舉例
! 非運算,表達式爲 true 則返回 false,否則返回 true。 [ ! false ] 返回 true。
-o 或運算,有一個表達式爲 true 則返回 true。 [ b -gt 100 ] 返回 true。
-a 與運算,兩個表達式都爲 true 才返回 true。 [ b -gt 100 ] 返回 false。

 

2 小於 5 或 3 大於 100 : 返回 true
#!/bin/bash

a=2
b=3

if [ $a != $b ]
then
   echo "$a != $b : a 不等於 b"
else
   echo "$a != $b: a 等於 b"
fi
if [ $a -lt 100 -a $b -gt 15 ]
then
   echo "$a 小於 100 且 $b 大於 15 : 返回 true"
else
   echo "$a 小於 100 且 $b 大於 15 : 返回 false"
fi
if [ $a -lt 100 -o $b -gt 100 ]
then
   echo "$a 小於 100 或 $b 大於 100 : 返回 true"
else
   echo "$a 小於 100 或 $b 大於 100 : 返回 false"
fi
if [ $a -lt 5 -o $b -gt 100 ]
then
   echo "$a 小於 5 或 $b 大於 100 : 返回 true"
else
   echo "$a 小於 5 或 $b 大於 100 : 返回 false"
fi


# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
2 != 3 : a 不等於 b
2 小於 100 且 3 大於 15 : 返回 false
2 小於 100 或 3 大於 100 : 返回 true
2 小於 5 或 3 大於 100 : 返回 true

邏輯運算符

運算符 說明 舉例
&& 邏輯的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
`   ` 邏輯的OR `[[ $a -lt 100   $b -gt 100 ]]` 返回 true

 

#!/bin/bash

a=2
b=3

if [[ $a -lt 5 && $b -gt 2 ]]
then
    echo "返回true"
else
    echo "返回false"
fi


if [[ $a -ge 2 || $b -le 3 ]]
then
    echo "返回true"
else
    echo "返回false"
fi


# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
返回true
返回true

字符串運算符

運算符 說明 舉例
= 檢測兩個字符串是否相等,相等返回 true。 [ b ] 返回 false。
!= 檢測兩個字符串是否相等,不相等返回 true。 [ b ] 返回 true。
-z 檢測字符串長度是否爲0,爲0返回 true。 [ -z $a ] 返回 false。
-n 檢測字符串長度是否爲0,不爲0返回 true。 [ -n "$a" ] 返回 true。
$ 檢測字符串是否爲空,不爲空返回 true。 [ $a ] 返回 true。

 

#!/bin/bash

a="abc"
b="efg"

if [ $a = $b ]
then
   echo "$a = $b : a 等於 b"
else
   echo "$a = $b: a 不等於 b"
fi
if [ $a != $b ]
then
   echo "$a != $b : a 不等於 b"
else
   echo "$a != $b: a 等於 b"
fi
if [ -z $a ]
then
   echo "-z $a : 字符串長度爲 0"
else
   echo "-z $a : 字符串長度不爲 0"
fi
if [ -n "$a" ]
then
   echo "-n $a : 字符串長度不爲 0"
else
   echo "-n $a : 字符串長度爲 0"
fi
if [ $a ]
then
   echo "$a : 字符串不爲空"
else
   echo "$a : 字符串爲空"
fi

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
abc = efg: a 不等於 b
abc != efg : a 不等於 b
-z abc : 字符串長度不爲 0
-n abc : 字符串長度不爲 0
abc : 字符串不爲空

文件測試運算符

文件測試運算符用於檢測Unix文件的各種屬性

操作符 說明 舉例
-b file 檢測文件是否是塊設備文件,如果是,則返回 true。 [ -b $file ] 返回 false。
-c file 檢測文件是否是字符設備文件,如果是,則返回 true。 [ -c $file ] 返回 false。
-d file 檢測文件是否是目錄,如果是,則返回 true。 [ -d $file ] 返回 false。
-f file 檢測文件是否是普通文件(既不是目錄,也不是設備文件),如果是,則返回 true。 [ -f $file ] 返回 true。
-g file 檢測文件是否設置了 SGID 位,如果是,則返回 true。 [ -g $file ] 返回 false。
-k file 檢測文件是否設置了粘着位(Sticky Bit),如果是,則返回 true。 [ -k $file ] 返回 false。
-p file 檢測文件是否是有名管道,如果是,則返回 true。 [ -p $file ] 返回 false。
-u file 檢測文件是否設置了 SUID 位,如果是,則返回 true。 [ -u $file ] 返回 false。
-r file 檢測文件是否可讀,如果是,則返回 true。 [ -r $file ] 返回 true。
-w file 檢測文件是否可寫,如果是,則返回 true。 [ -w $file ] 返回 true。
-x file 檢測文件是否可執行,如果是,則返回 true。 [ -x $file ] 返回 true。
-s file 檢測文件是否爲空(文件大小是否大於0),不爲空返回 true。 [ -s $file ] 返回 true。
-e file 檢測文件(包括目錄)是否存在,如果是,則返回 true。 [ -e $file ] 返回 true。

變量 file 表示文件"/home/user/test.sh",它的大小爲100字節,具有 rwx 權限。下面的代碼,將檢測該文件的各種屬性:

 

#!/bin/bash

file="/home/user/test.sh"
if [ -r $file ]
then
   echo "文件可讀"
else
   echo "文件不可讀"
fi
if [ -w $file ]
then
   echo "文件可寫"
else
   echo "文件不可寫"
fi
if [ -x $file ]
then
   echo "文件可執行"
else
   echo "文件不可執行"
fi
if [ -f $file ]
then
   echo "文件爲普通文件"
else
   echo "文件爲特殊文件"
fi
if [ -d $file ]
then
   echo "文件是個目錄"
else
   echo "文件不是個目錄"
fi
if [ -s $file ]
then
   echo "文件不爲空"
else
   echo "文件爲空"
fi
if [ -e $file ]
then
   echo "文件存在"
else
   echo "文件不存在"
fi

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
文件可讀
文件可寫
文件可執行
文件爲普通文件
文件不是個目錄
文件不爲空
文件存在

Shell內建(內置)命令

由 Bash 自身提供的命令,而不是文件系統中的某個可執行文件。

用於進入或者切換目錄的 cd 命令,雖然我們一直在使用它,但如果不加以注意很難意識到它與普通命令的性質是不一樣的:該命令並不是某個外部文件,只要在 Shell 中你就一定可以運行這個命令。

可以使用 type 來確定一個命令是否是內建命令:

 

user@PxeCtrlSys:~$ type cd
cd is a shell builtin  # 內建命令
user@PxeCtrlSys:~$ type ls
ls is aliased to 'ls --color=auto'
user@PxeCtrlSys:~$ type ssh
ssh is /usr/bin/ssh  # 外部命令

通常來說,內建命令會比外部命令執行得更快,執行外部命令時不但會觸發磁盤 I/O,還需要 fork 出一個單獨的進程來執行,執行完成後再退出。而執行內建命令相當於調用當前 Shell 進程的一個函數。

             
bash : . [ alias bg bind
break builtin cd command compgen complete continue
declare dirs disown echo enable eval exec
exit export fc fg getopts hash help
history jobs kill let local logout popd
printf pushd pwd read readonly return set
shift shopt source suspend test times trap
type typeset ulimit umask unalias unset wait

Shell alias:給命令創建別名

alias 用來給命令創建一個別名。

查看alias所有別名

若直接輸入該命令且不帶任何參數,則列出當前 Shell 環境中使用了哪些別名。

 

user@PxeCtrlSys:~$ alias
alias ls='ls --color=auto'

終於知道我的騰訊雲debian上ls命令沒有顏色區分了

 

# 沒有爲ls創建別名
root@StarMeow-Svr:~# alias
root@StarMeow-Svr:~# alias ls='ls --color=auto'
root@StarMeow-Svr:~# ls
# 這兒的文件和文件夾就有顏色區分了

使用 alias 當然也可以自定義別名,比如說一般的關機命令是shutdown-h now,寫起來比較長,這時可以重新定義一個關機命令,以後就方便多了。使用 alias 定義的別名命令也是支持 Tab 鍵補全的,如下所示:

 

alias myShutdown='shutdown -h now'

注意,這樣定義別名只能在當前 Shell 環境中有效,換句話說,重新登錄後這個別名就消失了。

永久生效alias別名

爲了確保永遠生效,可以將該別名手動寫入到用戶主目錄中的.bashrc文件。

 

root@StarMeow-Svr:~# vim /root/.bashrc 

# 將下方代碼取消被註釋
export LS_OPTIONS='--color=auto' 
eval "`dircolors`" 
alias ls='ls $LS_OPTIONS' 
alias ll='ls $LS_OPTIONS -l' 
alias l='ls $LS_OPTIONS -lA' 

# 修改完後使其生效
root@StarMeow-Svr:~# source ~/.bashrc 
root@StarMeow-Svr:~# ls
# 這兒的文件和文件夾就有顏色區分了

root@StarMeow-Svr:~# alias
alias l='ls $LS_OPTIONS -lA'
alias ll='ls $LS_OPTIONS -l'
alias ls='ls $LS_OPTIONS'

刪除alias別名

使用 unalias 內建命令可以刪除當前 Shell 環境中的別名。unalias 有兩種使用方法:

  • 第一種用法是在命令後跟上某個命令的別名,用於刪除指定的別名。
  • 第二種用法是在命令後接-a參數,刪除當前 Shell 環境中所有的別名。

同樣,這兩種方法都是在當前 Shell 環境中生效的。要想永久刪除在.bashrc文件中定義的別名,只能進入該文件手動刪除。

 

# 例如已經通過alias查到如下別名ls
user@PxeCtrlSys:~$ alias
alias ls='ls --color=auto'
# 使用unalias ls就可以刪除當前環境的別名
user@PxeCtrlSys:~$ unalias ls

Shell echo:輸出字符串

用於字符串輸出echo string

顯示普通字符串

 

echo "It is a string1"

這裏的雙引號可以完全省略

 

echo It is a string2

顯示轉義字符

 

#!/bin/bash

echo "\"It is a string1\""
echo \"It is a string2\"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
"It is a string1"
"It is a string2"

默認情況下,echo 不會解析以反斜槓\開頭的轉義字符。比如,\n表示換行,echo 默認會將它作爲普通字符對待。

 

echo "hello \nworld"
# 運行結果
hello \nworld

echo -e "hello \nworld"
# 運行結果
hello
world

同樣雙引號都是可以省略的

顯示變量

read命令從標準輸入中讀取一行,並把輸入行的每個字段的值指定給shell變量

 

#!/bin/bash

read name
echo "You entered $name"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
66
You entered 66

顯示換行:-e參數和\n

 

#!/bin/bash

echo -e "this is first line \n"
echo "next"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
this is first line 

next

輸出中-e表示開啓轉義,\n表示換行

顯示不換行:-e參數和\c或-n參數

 

#!/bin/bash

echo -e "this is first line \c"
echo "next"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
this is first line next

-e開啓轉義,\c表示不換行

 

echo -n "this is first line"
echo -n "next"

# 運行結果
this is first linenext

輸出結果重定向到文件

 

#!/bin/bash

echo -e "this is first line" > file.ts

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
user@PxeCtrlSys:~$ ls
file.ts software  test.sh
user@PxeCtrlSys:~$ cat file.ts 
this is first line

原樣輸出,不轉義,不取變量

直接使用單引號即可

 

#!/bin/bash

name='lr'
echo '$name"\'

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
$name"\

顯示命令執行結果

使用反引號,而不是單引號,可以執行Linux的命令

 

#!/bin/bash

echo `date`

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
Tue Mar 5 10:41:55 CST 2019

Shell exit:退出Shell命令

exit 是一個 Shell 內置命令,用來退出當前Shell:

  • 如果在終端中直接運行 exit 命令,會退出當前登錄的 Shell,並關閉終端;
  • 如果在Shell中出現 exit 命令,會停止執行後邊的所有代碼,立即退出 Shell 腳本。

exit 命令可以接受的參數是一個狀態值 n,代表退出時的狀態。如果不指定,默認狀態值是 0。

 

#!/bin/bash

echo "exit命令前輸出"
exit 9
echo "exit命令後輸出"

# 運行結果
user@PxeCtrlSys:~$ ./test.sh 
exit命令前輸出  # 也就是說exit後面的語句已經不會執行了

# 緊接着用 $? 來獲取test.sh的退出狀態
user@PxeCtrlSys:~$ echo $?
9

Shell ulimit:顯示並設置進程資源速度

系統的可用資源是有限的,如果不限制用戶和進程對系統資源的使用,則很容易陷入資源耗盡的地步,而使用 ulimit 命令可以控制進程對可用資源的訪問(ulimit 是一個 Shell 內置命令)。

默認情況下 Linux 系統的各個資源都做了軟硬限制,其中硬限制的作用是控制軟限制(換言之,軟限制不能高於硬限制)。使用ulimit -a可以查看當前系統的軟限制,使用命令ulimit -a –H可查看系統的硬限制。

ulimit -a查看軟限制

 

user@PxeCtrlSys:~$ ulimit -a
core file size          (blocks, -c) 0
# core文件大小,單位是block,默認爲0
data seg size           (kbytes, -d) unlimited
# 數據段大小,單位是kbyte,默認不做限制
scheduling priority             (-e) 0
# 調度優先級,默認爲0
file size               (blocks, -f) unlimited
# 創建文件的大小,單位是block,默認不做限制
pending signals                 (-i) 15596
# 掛起的信號數量,默認是8192
max locked memory       (kbytes, -l) 64
# 最大鎖定內存的值,單位是kbyte,默認是32
max memory size         (kbytes, -m) unlimited
# 最大可用的常駐內存值,單位是kbyte,默認不做限制
open files                      (-n) 65536
# 最大打開的文件數,默認是1024
pipe size            (512 bytes, -p) 8
# 管道最大緩衝區的值
POSIX message queues     (bytes, -q) 819200
# 消息隊列的最大值,單位是byte
real-time priority              (-r) 0
# 程序的實時性優先級,默認爲0
stack size              (kbytes, -s) 8192
# 棧大小,單位是kbyte
cpu time               (seconds, -t) unlimited
# 最大cpu佔用時間,默認不做限制
max user processes              (-u) 15596
# 用戶最大進程數,默認是8192
virtual memory          (kbytes, -v) unlimited
# 最大虛擬內存,單位是kbyte,默認不做限制
file locks                      (-x) unlimited
# 文件鎖,默認不做限制

每一行中都包含了相應的改變該項設置的參數,以最大可以打開的文件數爲例(open files 默認是 1024),想要增大至 4096 則按照如下命令設置(可參照此方法調整其他參數)。

 

# -n參數是設置最大文件打開數
# 下面命令會同時設置硬限制和軟限制
user@PxeCtrlSys:~$ ulimit -n 65536

# 使用-S參數單獨設置軟限制
user@PxeCtrlSys:~$ ulimit -S -n 65536

# 使用-H參數單獨設置硬限制
user@PxeCtrlSys:~$ ulimit -H -n 65536

limits.conf 配置文件

使用 ulimit 直接調整參數,只會在當前運行時生效,一旦系統重啓,所有調整過的參數就會變回系統默認值。所以建議將所有的改動放在 ulimit 的系統配置文件中。

 

user@PxeCtrlSys:~$ cat /etc/security/limits.conf 
# /etc/security/limits.conf
#該文件是ulimit的配置文件,任何對系統的ulimit的修改都應寫入該文件
#Each line describes a limit for a user in the form:
#配置應該寫成西面格式,即每個配置佔用1行,每行4列
#<domain>        <type>  <item>  <value>
#
#Where:
#<domain>取值如下:一個用戶名、一個組名,組名前面用@符號、通配符*、通配符%
#<domain> can be:
#        - a user name
#        - a group name, with @group syntax
#        - the wildcard *, for default entry
#        - the wildcard %, can be also used with %group syntax,
#                 for maxlogin limit
#        - NOTE: group and wildcard limits are not applied to root.
#          To apply a limit to the root user, <domain> must be
#          the literal username root.
#
#<type>有兩個可用值:soft用於設置軟限制、hard用於設置硬限制
#<type> can have the two values:
#        - "soft" for enforcing the soft limits
#        - "hard" for enforcing hard limits
#
#<item> can be one of the following:
#        - core - limits the core file size (KB)
#        - data - max data size (KB)
#        - fsize - maximum filesize (KB)
#        - memlock - max locked-in-memory address space (KB)
#        - nofile - max number of open files
#        - rss - max resident set size (KB)
#        - stack - max stack size (KB)
#        - cpu - max CPU time (MIN)
#        - nproc - max number of processes
#        - as - address space limit (KB)
#        - maxlogins - max number of logins for this user
#        - maxsyslogins - max number of logins on the system
#        - priority - the priority to run user process with
#        - locks - max number of file locks the user can hold
#        - sigpending - max number of pending signals
#        - msgqueue - max memory used by POSIX message queues (bytes)
#        - nice - max nice priority allowed to raise to values: [-20, 19]
#        - rtprio - max realtime priority
#        - chroot - change root to directory (Debian-specific)
#
#<domain>      <type>  <item>         <value>
#
#以下是使用樣例
#*               soft    core            0
#root            hard    core            100000
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#ftp             -       chroot          /ftp
#@student        -       maxlogins       4

# End of file


 

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