軟件源碼購買一般在哪個網(wǎng)站網(wǎng)址網(wǎng)域ip地址查詢
大家好,這里是 Lucifer三思而后行,專注于提升數(shù)據(jù)庫運維效率。
文章目錄
- 一篇文章讓你徹底掌握 Shell
- 簡介
- 什么是 shell
- 什么是 shell 腳本
- Shell 環(huán)境
- 指定腳本解釋器
- 模式
- 交互模式
- 非交互模式
- 基本語法
- 解釋器
- 注釋
- echo
- printf
- printf 的轉(zhuǎn)義符
- 變量
- 變量命名原則
- 聲明變量
- 只讀變量
- 刪除變量
- 變量類型
- 字符串
- 單引號和雙引號
- 拼接字符串
- 獲取字符串長度
- 截取子字符串
- 查找子字符串
- 數(shù)組
- 創(chuàng)建數(shù)組
- 訪問數(shù)組元素
- 訪問數(shù)組的單個元素
- 訪問數(shù)組的所有元素
- 訪問數(shù)組的部分元素
- 訪問數(shù)組長度
- 向數(shù)組中添加元素
- 從數(shù)組中刪除元素
- 運算符
- 算術(shù)運算符
- 關(guān)系運算符
- 布爾運算符
- 邏輯運算符
- 字符串運算符
- 文件測試運算符
- 控制語句
- 條件語句
- if
- case
- 循環(huán)語句
- for 循環(huán)
- while 循環(huán)
- until 循環(huán)
- select 循環(huán)
- break 和 continue
- 函數(shù)
- 位置參數(shù)
- 函數(shù)處理參數(shù)
- Shell 擴展
- 大括號擴展
- 命令置換
- 算數(shù)擴展
- 單引號和雙引號
- 流和重定向
- 輸入、輸出流
- 重定向
- /dev/null 文件
- Debug
- 往期精彩文章
一篇文章讓你徹底掌握 Shell
Bash 是 Linux 標(biāo)準默認的 shell 解釋器,可以說 bash 是 shell 編程的基礎(chǔ)。
本文主要介紹 bash 的語法,對于 Linux 指令不做任何介紹。
███████╗██╗ ██╗███████╗██╗ ██╗
██╔════╝██║ ██║██╔════╝██║ ██║
███████╗███████║█████╗ ██║ ██║
╚════██║██╔══██║██╔══╝ ██║ ██║
███████║██║ ██║███████╗███████╗███████╗
╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝
簡介
什么是 shell
- Shell 是一個用 C 語言編寫的程序,它是用戶使用 Linux 的橋梁。
- Shell 既是一種命令語言,又是一種程序設(shè)計語言。
- Shell 是指一種應(yīng)用程序,這個應(yīng)用程序提供了一個界面,用戶通過這個界面訪問 Linux 內(nèi)核的服務(wù)。
Ken Thompson 的 sh 是第一種 Unix Shell,Windows Explorer 是一個典型的圖形界面 Shell。
什么是 shell 腳本
Shell 腳本(shell script),是一種為 shell 編寫的腳本程序,一般文件后綴為 .sh
。
業(yè)界所說的 shell 通常都是指 shell 腳本,但 shell 和 shell script 是兩個不同的概念。
Shell 環(huán)境
Shell 編程跟 java、php 編程一樣,只要有一個能編寫代碼的文本編輯器和一個能解釋執(zhí)行的腳本解釋器就可以了。
Shell 的解釋器種類眾多,常見的有:
- sh - 即 Bourne Shell。sh 是 Unix 標(biāo)準默認的 shell。
- bash - 即 Bourne Again Shell。bash 是 Linux 標(biāo)準默認的 shell。
- fish - 智能和用戶友好的命令行 shell。
- xiki - 使 shell 控制臺更友好,更強大。
- zsh - 功能強大的 shell 與腳本語言。
指定腳本解釋器
在 shell 腳本,#!
告訴系統(tǒng)其后路徑所指定的程序即是解釋此腳本文件的 Shell 解釋器。#!
被稱作 shebang(也稱為 Hashbang )。
所以,你應(yīng)該會在 shell 中,見到諸如以下的注釋:
指定 sh 解釋器:
#!/bin/sh
指定 bash 解釋器:
#!/bin/bash
注意: 上面的指定解釋器的方式是比較常見的,但有時候,你可能也會看到下面的方式:
#!/usr/bin/env bash
這樣做的好處是,系統(tǒng)會自動在 PATH 環(huán)境變量中查找你指定的程序(本例中的 bash)。相比第一種寫法,你應(yīng)該盡量用這種寫法,因為程序的路徑是不確定的。這樣寫還有一個好處,操作系統(tǒng)的 PATH 變量有可能被配置為指向程序的另一個版本。比如,安裝完新版本的 bash,我們可能將其路徑添加到 PATH 中,來“隱藏”老版本。如果直接用 #!/bin/bash
,那么系統(tǒng)會選擇老版本的 bash 來執(zhí)行腳本,如果用 #!/usr/bin/env bash
,則會使用新版本。
模式
shell 有交互和非交互兩種模式。
交互模式
簡單來說,你可以將 shell 的交互模式理解為執(zhí)行命令行。
看到形如下面的東西,說明 shell 處于交互模式下:
user@host:~$
接著,便可以輸入一系列 Linux 命令,比如 ls,grep,cd,mkdir,rm 等等。
非交互模式
簡單來說,你可以將 shell 的非交互模式理解為執(zhí)行 shell 腳本。
在非交互模式下,shell 從文件或者管道中讀取命令并執(zhí)行。
當(dāng) shell 解釋器執(zhí)行完文件中的最后一個命令,shell 進程終止,并回到父進程。
可以使用下面的命令讓 shell 以非交互模式運行:
sh /path/to/script.sh
bash /path/to/script.sh
source /path/to/script.sh
./path/to/script.sh
上面的例子中,script.sh 是一個包含 shell 解釋器可以識別并執(zhí)行的命令的普通文本文件,sh 和 bash 是 shell 解釋器程序。你可以使用任何喜歡的編輯器創(chuàng)建 script.sh(vim,nano,Sublime Text, Atom 等等)。
其中,source /path/to/script.sh
和 ./path/to/script.sh
是等價的。
除此之外,你還可以通過 chmod 命令給文件添加可執(zhí)行的權(quán)限,來直接執(zhí)行腳本文件:
chmod +x /path/to/script.sh #使腳本具有執(zhí)行權(quán)限
/path/to/test.sh
這種方式要求腳本文件的第一行必須指明運行該腳本的程序,比如:
💻 “示例源碼”:
#!/usr/bin/env bash
echo "Hello, world!"
上面的例子中,我們使用了一個很有用的命令 echo 來輸出字符串到屏幕上。
基本語法
解釋器
前面雖然兩次提到了 #!
,但是本著重要的事情說三遍的精神,這里再強調(diào)一遍:
在 shell 腳本,#!
告訴系統(tǒng)其后路徑所指定的程序即是解釋此腳本文件的 Shell 解釋器。#!
被稱作 shebang(也稱為 Hashbang )。
#!
決定了腳本可以像一個獨立的可執(zhí)行文件一樣執(zhí)行,而不用在終端之前輸入 sh, bash, python, php 等。
# 以下兩種方式都可以指定 shell 解釋器為 bash,第二種方式更好
#!/bin/bash
#!/usr/bin/env bash
注釋
注釋可以說明你的代碼是什么作用,以及為什么這樣寫。
shell 語法中,注釋是特殊的語句,會被 shell 解釋器忽略。
- 單行注釋 - 以
#
開頭,到行尾結(jié)束。 - 多行注釋 - 以
:<<EOF
開頭,到EOF
結(jié)束。
💻 “示例源碼”:
#--------------------------------------------
# shell 注釋示例
# author:zp
#--------------------------------------------# echo '這是單行注釋'########## 這是分割線 ##########:<<EOF
echo '這是多行注釋'
echo '這是多行注釋'
echo '這是多行注釋'
EOF
echo
echo 用于字符串的輸出。
輸出普通字符串:
echo "hello, world"
# Output: hello, world
輸出含變量的字符串:
echo "hello, \"zp\""
# Output: hello, "zp"
輸出含變量的字符串:
name=zp
echo "hello, \"${name}\""
# Output: hello, "zp"
輸出含換行符的字符串:
# 輸出含換行符的字符串
echo "YES\nNO"
# Output: YES\nNOecho -e "YES\nNO" # -e 開啟轉(zhuǎn)義
# Output:
# YES
# NO
輸出含不換行符的字符串:
echo "YES"
echo "NO"
# Output:
# YES
# NOecho -e "YES\c" # -e 開啟轉(zhuǎn)義 \c 不換行
echo "NO"
# Output:
# YESNO
輸出重定向至文件:
echo "test" > test.txt
輸出執(zhí)行結(jié)果:
echo `pwd`
# Output:(當(dāng)前目錄路徑)
💻 “示例源碼”:
#!/usr/bin/env bash# 輸出普通字符串
echo "hello, world"
# Output: hello, world# 輸出含變量的字符串
echo "hello, \"zp\""
# Output: hello, "zp"# 輸出含變量的字符串
name=zp
echo "hello, \"${name}\""
# Output: hello, "zp"# 輸出含換行符的字符串
echo "YES\nNO"
# Output: YES\nNO
echo -e "YES\nNO" # -e 開啟轉(zhuǎn)義
# Output:
# YES
# NO# 輸出含不換行符的字符串
echo "YES"
echo "NO"
# Output:
# YES
# NOecho -e "YES\c" # -e 開啟轉(zhuǎn)義 \c 不換行
echo "NO"
# Output:
# YESNO# 輸出內(nèi)容定向至文件
echo "test" > test.txt# 輸出執(zhí)行結(jié)果
echo `pwd`
# Output:(當(dāng)前目錄路徑)
printf
printf 用于格式化輸出字符串。
默認,printf 不會像 echo 一樣自動添加換行符,如果需要換行可以手動添加 \n。
💻 “示例源碼”:
# 單引號
printf '%d %s\n' 1 "abc"
# Output:1 abc# 雙引號
printf "%d %s\n" 1 "abc"
# Output:1 abc# 無引號
printf %s abcdef
# Output: abcdef(并不會換行)# 格式只指定了一個參數(shù),但多出的參數(shù)仍然會按照該格式輸出
printf "%s\n" abc def
# Output:
# abc
# defprintf "%s %s %s\n" a b c d e f g h i j
# Output:
# a b c
# d e f
# g h i
# j# 如果沒有參數(shù),那么 %s 用 NULL 代替,%d 用 0 代替
printf "%s and %d \n"
# Output:
# and 0# 格式化輸出
printf "%-10s %-8s %-4s\n" 姓名 性別 體重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 楊過 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
# Output:
# 姓名 性別 體重kg
# 郭靖 男 66.12
# 楊過 男 48.65
# 郭芙 女 47.99
printf 的轉(zhuǎn)義符
序列 | 說明 |
---|---|
\a | 警告字符,通常為 ASCII 的 BEL 字符 |
\b | 后退 |
\c | 抑制(不顯示)輸出結(jié)果中任何結(jié)尾的換行字符(只在%b 格式指示符控制下的參數(shù)字符串中有效),而且,任何留在參數(shù)里的字符、任何接下來的參數(shù)以及任何留在格式字符串中的字符,都被忽略 |
\f | 換頁(formfeed) |
\n | 換行 |
\r | 回車(Carriage return) |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 一個字面上的反斜杠字符 |
\ddd | 表示 1 到 3 位數(shù)八進制值的字符。僅在格式字符串中有效 |
\0ddd | 表示 1 到 3 位的八進制值字符 |
變量
跟許多程序設(shè)計語言一樣,你可以在 bash 中創(chuàng)建變量。
Bash 中沒有數(shù)據(jù)類型,bash 中的變量可以保存一個數(shù)字、一個字符、一個字符串等等。同時無需提前聲明變量,給變量賦值會直接創(chuàng)建變量。
變量命名原則
- 命名只能使用英文字母,數(shù)字和下劃線,首個字符不能以數(shù)字開頭。
- 中間不能有空格,可以使用下劃線(_)。
- 不能使用標(biāo)點符號。
- 不能使用 bash 里的關(guān)鍵字(可用 help 命令查看保留關(guān)鍵字)。
聲明變量
訪問變量的語法形式為:${var}
和 $var
。
變量名外面的花括號是可選的,加不加都行,加花括號是為了幫助解釋器識別變量的邊界,所以推薦加花括號。
word="hello"
echo ${word}
# Output: hello
只讀變量
使用 readonly 命令可以將變量定義為只讀變量,只讀變量的值不能被改變。
rword="hello"
echo ${rword}
readonly rword
# rword="bye" # 如果放開注釋,執(zhí)行時會報錯
刪除變量
使用 unset 命令可以刪除變量。變量被刪除后不能再次使用。unset 命令不能刪除只讀變量。
dword="hello" # 聲明變量
echo ${dword} # 輸出變量值
# Output: hellounset dword # 刪除變量
echo ${dword}
# Output: (空)
變量類型
- 局部變量 - 局部變量是僅在某個腳本內(nèi)部有效的變量。它們不能被其他的程序和腳本訪問。
- 環(huán)境變量 - 環(huán)境變量是對當(dāng)前 shell 會話內(nèi)所有的程序或腳本都可見的變量。創(chuàng)建它們跟創(chuàng)建局部變量類似,但使用的是 export 關(guān)鍵字,shell 腳本也可以定義環(huán)境變量。
常見的環(huán)境變量:
變量 | 描述 |
---|---|
$HOME | 當(dāng)前用戶的用戶目錄 |
$PATH | 用分號分隔的目錄列表,shell 會到這些目錄中查找命令 |
$PWD | 當(dāng)前工作目錄 |
$RANDOM | 0 到 32767 之間的整數(shù) |
$UID | 數(shù)值類型,當(dāng)前用戶的用戶 ID |
$PS1 | 主要系統(tǒng)輸入提示符 |
$PS2 | 次要系統(tǒng)輸入提示符 |
這里 有一張更全面的 Bash 環(huán)境變量列表。
💻 “示例源碼”:
#!/usr/bin/env bash################### 聲明變量 ###################
name="world"
echo "hello ${name}"
# Output: hello world################### 輸出變量 ###################
folder=$(pwd)
echo "current path: ${folder}"################### 只讀變量 ###################
rword="hello"
echo ${rword}
# Output: hello
readonly rword
# rword="bye" # 如果放開注釋,執(zhí)行時會報錯################### 刪除變量 ###################
dword="hello" # 聲明變量
echo ${dword} # 輸出變量值
# Output: hellounset dword # 刪除變量
echo ${dword}
# Output: (空)################### 系統(tǒng)變量 ###################
echo "UID:$UID"
echo LOGNAME:$LOGNAME
echo User:$USER
echo HOME:$HOME
echo PATH:$PATH
echo HOSTNAME:$HOSTNAME
echo SHELL:$SHELL
echo LANG:$LANG################### 自定義變量 ###################
days=10
user="admin"
echo "$user logged in $days days age"
days=5
user="root"
echo "$user logged in $days days age"
# Output:
# admin logged in 10 days age
# root logged in 5 days age################### 從變量讀取列表 ###################
colors="Red Yellow Blue"
colors=$colors" White Black"for color in $colors
doecho " $color"
done
字符串
單引號和雙引號
shell 字符串可以用單引號 ‘’,也可以用雙引號 “”,也可以不用引號。
- 單引號的特點 - 單引號里不識別變量 - 單引號里不能出現(xiàn)單獨的單引號(使用轉(zhuǎn)義符也不行),但可成對出現(xiàn),作為字符串拼接使用。
- 雙引號的特點 - 雙引號里識別變量 - 雙引號里可以出現(xiàn)轉(zhuǎn)義字符
綜上,推薦使用雙引號。
拼接字符串
# 使用單引號拼接
name1='white'
str1='hello, '${name1}''
str2='hello, ${name1}'
echo ${str1}_${str2}
# Output:
# hello, white_hello, ${name1}# 使用雙引號拼接
name2="black"
str3="hello, "${name2}""
str4="hello, ${name2}"
echo ${str3}_${str4}
# Output:
# hello, black_hello, black
獲取字符串長度
text="12345"
echo ${#text}
# Output:
# 5
截取子字符串
text="12345"
echo ${text:2:2}
# Output:
# 34
從第 3 個字符開始,截取 2 個字符。
查找子字符串
#!/usr/bin/env bashtext="hello"
echo `expr index "${text}" ll`# Execute: ./str-demo5.sh
# Output:
# 3
查找 ll 子字符在 hello 字符串中的起始位置。
💻 “示例源碼”:
#!/usr/bin/env bash################### 使用單引號拼接字符串 ###################
name1='white'
str1='hello, '${name1}''
str2='hello, ${name1}'
echo ${str1}_${str2}
# Output:
# hello, white_hello, ${name1}################### 使用雙引號拼接字符串 ###################
name2="black"
str3="hello, "${name2}""
str4="hello, ${name2}"
echo ${str3}_${str4}
# Output:
# hello, black_hello, black################### 獲取字符串長度 ###################
text="12345"
echo "${text} length is: ${#text}"
# Output:
# 12345 length is: 5# 獲取子字符串
text="12345"
echo ${text:2:2}
# Output:
# 34################### 查找子字符串 ###################
text="hello"
echo `expr index "${text}" ll`
# Output:
# 3################### 判斷字符串中是否包含子字符串 ###################
result=$(echo "${str}" | grep "feature/")
if [[ "$result" != "" ]]; thenecho "feature/ 是 ${str} 的子字符串"
elseecho "feature/ 不是 ${str} 的子字符串"
fi################### 截取關(guān)鍵字左邊內(nèi)容 ###################
full_branch="feature/1.0.0"
branch=`echo ${full_branch#feature/}`
echo "branch is ${branch}"################### 截取關(guān)鍵字右邊內(nèi)容 ###################
full_version="0.0.1-SNAPSHOT"
version=`echo ${full_version%-SNAPSHOT}`
echo "version is ${version}"################### 字符串分割成數(shù)組 ###################
str="0.0.0.1"
OLD_IFS="$IFS"
IFS="."
array=( ${str} )
IFS="$OLD_IFS"
size=${#array[*]}
lastIndex=`expr ${size} - 1`
echo "數(shù)組長度:${size}"
echo "最后一個數(shù)組元素:${array[${lastIndex}]}"
for item in ${array[@]}
doecho "$item"
done################### 判斷字符串是否為空 ###################
#-n 判斷長度是否非零
#-z 判斷長度是否為零str=testing
str2=''
if [[ -n "$str" ]]
thenecho "The string $str is not empty"
elseecho "The string $str is empty"
fiif [[ -n "$str2" ]]
thenecho "The string $str2 is not empty"
elseecho "The string $str2 is empty"
fi# Output:
# The string testing is not empty
# The string is empty################### 字符串比較 ###################
str=hello
str2=world
if [[ $str = "hello" ]]; thenecho "str equals hello"
elseecho "str not equals hello"
fiif [[ $str2 = "hello" ]]; thenecho "str2 equals hello"
elseecho "str2 not equals hello"
fi
數(shù)組
bash 只支持一維數(shù)組。
數(shù)組下標(biāo)從 0 開始,下標(biāo)可以是整數(shù)或算術(shù)表達式,其值應(yīng)大于或等于 0。
創(chuàng)建數(shù)組
# 創(chuàng)建數(shù)組的不同方式
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")
訪問數(shù)組元素
訪問數(shù)組的單個元素
echo ${nums[1]}
# Output: 1
訪問數(shù)組的所有元素
echo ${colors[*]}
# Output: red yellow dark blueecho ${colors[@]}
# Output: red yellow dark blue
上面兩行有很重要(也很微妙)的區(qū)別:
為了將數(shù)組中每個元素單獨一行輸出,我們用 printf 命令:
printf "+ %s\n" ${colors[*]}
# Output:
# + red
# + yellow
# + dark
# + blue
為什么 dark 和 blue 各占了一行?嘗試用引號包起來:
printf "+ %s\n" "${colors[*]}"
# Output:
# + red yellow dark blue
現(xiàn)在所有的元素都在一行輸出 —— 這不是我們想要的!讓我們試試 ${colors[@]}
printf "+ %s\n" "${colors[@]}"
# Output:
# + red
# + yellow
# + dark blue
在引號內(nèi),${colors[@]}將數(shù)組中的每個元素擴展為一個單獨的參數(shù);數(shù)組元素中的空格得以保留。
訪問數(shù)組的部分元素
echo ${nums[@]:0:2}
# Output:
# 0 1
在上面的例子中,${array[@]} 擴展為整個數(shù)組,:0:2 取出了數(shù)組中從 0 開始,長度為 2 的元素。
訪問數(shù)組長度
echo ${#nums[*]}
# Output:
# 3
向數(shù)組中添加元素
向數(shù)組中添加元素也非常簡單:
colors=(white "${colors[@]}" green black)
echo ${colors[@]}
# Output:
# white red yellow dark blue green black
上面的例子中,${colors[@]} 擴展為整個數(shù)組,并被置換到復(fù)合賦值語句中,接著,對數(shù)組 colors 的賦值覆蓋了它原來的值。
從數(shù)組中刪除元素
用 unset 命令來從數(shù)組中刪除一個元素:
unset nums[0]
echo ${nums[@]}
# Output:
# 1 2
💻 “示例源碼”:
#!/usr/bin/env bash################### 創(chuàng)建數(shù)組 ###################
nums=( [ 2 ] = 2 [ 0 ] = 0 [ 1 ] = 1 )
colors=( red yellow "dark blue" )################### 訪問數(shù)組的單個元素 ###################
echo ${nums[1]}
# Output: 1################### 訪問數(shù)組的所有元素 ###################
echo ${colors[*]}
# Output: red yellow dark blueecho ${colors[@]}
# Output: red yellow dark blueprintf "+ %s\n" ${colors[*]}
# Output:
# + red
# + yellow
# + dark
# + blueprintf "+ %s\n" "${colors[*]}"
# Output:
# + red yellow dark blueprintf "+ %s\n" "${colors[@]}"
# Output:
# + red
# + yellow
# + dark blue################### 訪問數(shù)組的部分元素 ###################
echo ${nums[@]:0:2}
# Output:
# 0 1################### 獲取數(shù)組長度 ###################
echo ${#nums[*]}
# Output:
# 3################### 向數(shù)組中添加元素 ###################
colors=( white "${colors[@]}" green black )
echo ${colors[@]}
# Output:
# white red yellow dark blue green black################### 從數(shù)組中刪除元素 ###################
unset nums[ 0 ]
echo ${nums[@]}
# Output:
# 1 2
運算符
算術(shù)運算符
下表列出了常用的算術(shù)運算符,假定變量 x 為 10,變量 y 為 20:
運算符 | 說明 | 舉例 |
---|---|---|
+ | 加法 | expr $x + $y 結(jié)果為 30。 |
- | 減法 | expr $x - $y 結(jié)果為 -10。 |
* | 乘法 | expr $x * $y 結(jié)果為 200。 |
/ | 除法 | expr $y / $x 結(jié)果為 2。 |
% | 取余 | expr $y % $x 結(jié)果為 0。 |
= | 賦值 | x=$y 將把變量 y 的值賦給 x。 |
== | 相等。用于比較兩個數(shù)字,相同則返回 true。 | [ $x == $y ] 返回 false。 |
!= | 不相等。用于比較兩個數(shù)字,不相同則返回 true。 | [ $x != $y ] 返回 true。 |
注意:條件表達式要放在方括號之間,并且要有空格,例如: [ x = = x== x==y] 是錯誤的,必須寫成 [ $x == $y ]。
💻 “示例源碼”:
x=10
y=20echo "x=${x}, y=${y}"val=`expr ${x} + ${y}`
echo "${x} + ${y} = $val"val=`expr ${x} - ${y}`
echo "${x} - ${y} = $val"val=`expr ${x} \* ${y}`
echo "${x} * ${y} = $val"val=`expr ${y} / ${x}`
echo "${y} / ${x} = $val"val=`expr ${y} % ${x}`
echo "${y} % ${x} = $val"if [[ ${x} == ${y} ]]
thenecho "${x} = ${y}"
fi
if [[ ${x} != ${y} ]]
thenecho "${x} != ${y}"
fi# Output:
# x=10, y=20
# 10 + 20 = 30
# 10 - 20 = -10
# 10 * 20 = 200
# 20 / 10 = 2
# 20 % 10 = 0
# 10 != 20
關(guān)系運算符
關(guān)系運算符只支持數(shù)字,不支持字符串,除非字符串的值是數(shù)字。
下表列出了常用的關(guān)系運算符,假定變量 x 為 10,變量 y 為 20:
運算符 | 說明 | 舉例 |
---|---|---|
-eq | 檢測兩個數(shù)是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne | 檢測兩個數(shù)是否相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt | 檢測左邊的數(shù)是否大于右邊的,如果是,則返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt | 檢測左邊的數(shù)是否小于右邊的,如果是,則返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge | 檢測左邊的數(shù)是否大于等于右邊的,如果是,則返回 true。 | [ $a -ge $b ] 返回 false。 |
-le | 檢測左邊的數(shù)是否小于等于右邊的,如果是,則返回 true。 | [ $a -le $b ] 返回 true。 |
💻 “示例源碼”:
x=10
y=20echo "x=${x}, y=${y}"if [[ ${x} -eq ${y} ]]; thenecho "${x} -eq ${y} : x 等于 y"
elseecho "${x} -eq ${y}: x 不等于 y"
fiif [[ ${x} -ne ${y} ]]; thenecho "${x} -ne ${y}: x 不等于 y"
elseecho "${x} -ne ${y}: x 等于 y"
fiif [[ ${x} -gt ${y} ]]; thenecho "${x} -gt ${y}: x 大于 y"
elseecho "${x} -gt ${y}: x 不大于 y"
fiif [[ ${x} -lt ${y} ]]; thenecho "${x} -lt ${y}: x 小于 y"
elseecho "${x} -lt ${y}: x 不小于 y"
fiif [[ ${x} -ge ${y} ]]; thenecho "${x} -ge ${y}: x 大于或等于 y"
elseecho "${x} -ge ${y}: x 小于 y"
fiif [[ ${x} -le ${y} ]]; thenecho "${x} -le ${y}: x 小于或等于 y"
elseecho "${x} -le ${y}: x 大于 y"
fi# Output:
# x=10, y=20
# 10 -eq 20: x 不等于 y
# 10 -ne 20: x 不等于 y
# 10 -gt 20: x 不大于 y
# 10 -lt 20: x 小于 y
# 10 -ge 20: x 小于 y
# 10 -le 20: x 小于或等于 y
布爾運算符
下表列出了常用的布爾運算符,假定變量 x 為 10,變量 y 為 20:
運算符 | 說明 | 舉例 |
---|---|---|
! | 非運算,表達式為 true 則返回 false,否則返回 true。 | [ ! false ] 返回 true。 |
-o | 或運算,有一個表達式為 true 則返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 與運算,兩個表達式都為 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
💻 “示例源碼”:
x=10
y=20echo "x=${x}, y=${y}"if [[ ${x} != ${y} ]]; thenecho "${x} != ${y} : x 不等于 y"
elseecho "${x} != ${y}: x 等于 y"
fiif [[ ${x} -lt 100 && ${y} -gt 15 ]]; thenecho "${x} 小于 100 且 ${y} 大于 15 : 返回 true"
elseecho "${x} 小于 100 且 ${y} 大于 15 : 返回 false"
fiif [[ ${x} -lt 100 || ${y} -gt 100 ]]; thenecho "${x} 小于 100 或 ${y} 大于 100 : 返回 true"
elseecho "${x} 小于 100 或 ${y} 大于 100 : 返回 false"
fiif [[ ${x} -lt 5 || ${y} -gt 100 ]]; thenecho "${x} 小于 5 或 ${y} 大于 100 : 返回 true"
elseecho "${x} 小于 5 或 ${y} 大于 100 : 返回 false"
fi# Output:
# x=10, y=20
# 10 != 20 : x 不等于 y
# 10 小于 100 且 20 大于 15 : 返回 true
# 10 小于 100 或 20 大于 100 : 返回 true
# 10 小于 5 或 20 大于 100 : 返回 false
邏輯運算符
以下介紹 Shell 的邏輯運算符,假定變量 x 為 10,變量 y 為 20:
運算符 | 說明 | 舉例 |
---|---|---|
&& | 邏輯的 AND | [[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false |
` | ` |
💻 “示例源碼”:
x=10
y=20echo "x=${x}, y=${y}"if [[ ${x} -lt 100 && ${y} -gt 100 ]]
thenecho "${x} -lt 100 && ${y} -gt 100 返回 true"
elseecho "${x} -lt 100 && ${y} -gt 100 返回 false"
fiif [[ ${x} -lt 100 || ${y} -gt 100 ]]
thenecho "${x} -lt 100 || ${y} -gt 100 返回 true"
elseecho "${x} -lt 100 || ${y} -gt 100 返回 false"
fi# Output:
# x=10, y=20
# 10 -lt 100 && 20 -gt 100 返回 false
# 10 -lt 100 || 20 -gt 100 返回 true
字符串運算符
下表列出了常用的字符串運算符,假定變量 a 為 “abc”,變量 b 為 “efg”:
運算符 | 說明 | 舉例 |
---|---|---|
= | 檢測兩個字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 檢測兩個字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 檢測字符串長度是否為 0,為 0 返回 true。 | [ -z $a ] 返回 false。 |
-n | 檢測字符串長度是否為 0,不為 0 返回 true。 | [ -n $a ] 返回 true。 |
str | 檢測字符串是否為空,不為空返回 true。(注:這可能是一個誤解,因為str 不是標(biāo)準的 shell 測試運算符。正確的可能是 -n 或 [[ ]] 測試。) | [ $a ] 返回 true。 |
💻 “示例源碼”:
x="abc"
y="xyz"echo "x=${x}, y=${y}"if [[ ${x} = ${y} ]]; thenecho "${x} = ${y} : x 等于 y"
elseecho "${x} = ${y}: x 不等于 y"
fiif [[ ${x} != ${y} ]]; thenecho "${x} != ${y} : x 不等于 y"
elseecho "${x} != ${y}: x 等于 y"
fiif [[ -z ${x} ]]; thenecho "-z ${x} : 字符串長度為 0"
elseecho "-z ${x} : 字符串長度不為 0"
fiif [[ -n "${x}" ]]; thenecho "-n ${x} : 字符串長度不為 0"
elseecho "-n ${x} : 字符串長度為 0"
fiif [[ ${x} ]]; thenecho "${x} : 字符串不為空"
elseecho "${x} : 字符串為空"
fi# Output:
# x=abc, y=xyz
# abc = xyz: x 不等于 y
# abc != xyz : x 不等于 y
# -z abc : 字符串長度不為 0
# -n abc : 字符串長度不為 0
# abc : 字符串不為空
文件測試運算符
文件測試運算符用于檢測 Unix 文件的各種屬性。
屬性檢測描述如下:
操作符 | 說明 | 舉例 |
---|---|---|
-b | 檢測文件是否是塊設(shè)備文件,如果是,則返回 true。 | [ -b $file ] 返回 false。 |
-c | 檢測文件是否是字符設(shè)備文件,如果是,則返回 true。 | [ -c $file ] 返回 false。 |
-d | 檢測文件是否是目錄,如果是,則返回 true。 | [ -d $file ] 返回 false。 |
-f | 檢測文件是否是普通文件(既不是目錄,也不是設(shè)備文件),如果是,則返回 true。 | [ -f $file ] 返回 true。 |
-g | 檢測文件是否設(shè)置了 SGID 位,如果是,則返回 true。 | [ -g $file ] 返回 false。 |
-k | 檢測文件是否設(shè)置了粘著位(Sticky Bit),如果是,則返回 true。 | [ -k $file ] 返回 false。 |
-p | 檢測文件是否是有名管道,如果是,則返回 true。 | [ -p $file ] 返回 false。 |
-u | 檢測文件是否設(shè)置了 SUID 位,如果是,則返回 true。 | [ -u $file ] 返回 false。 |
-r | 檢測文件是否可讀,如果是,則返回 true。 | [ -r $file ] 返回 true。 |
-w | 檢測文件是否可寫,如果是,則返回 true。 | [ -w $file ] 返回 true。 |
-x | 檢測文件是否可執(zhí)行,如果是,則返回 true。 | [ -x $file ] 返回 true。 |
-s | 檢測文件是否為空(文件大小是否大于 0),不為空返回 true。 | [ -s $file ] 返回 true。 |
-e | 檢測文件(包括目錄)是否存在,如果是,則返回 true。 | [ -e $file ] 返回 true。 |
💻 “示例源碼”:
file="/etc/hosts"if [[ -r ${file} ]]; thenecho "${file} 文件可讀"
elseecho "${file} 文件不可讀"
fi
if [[ -w ${file} ]]; thenecho "${file} 文件可寫"
elseecho "${file} 文件不可寫"
fi
if [[ -x ${file} ]]; thenecho "${file} 文件可執(zhí)行"
elseecho "${file} 文件不可執(zhí)行"
fi
if [[ -f ${file} ]]; thenecho "${file} 文件為普通文件"
elseecho "${file} 文件為特殊文件"
fi
if [[ -d ${file} ]]; thenecho "${file} 文件是個目錄"
elseecho "${file} 文件不是個目錄"
fi
if [[ -s ${file} ]]; thenecho "${file} 文件不為空"
elseecho "${file} 文件為空"
fi
if [[ -e ${file} ]]; thenecho "${file} 文件存在"
elseecho "${file} 文件不存在"
fi# Output:(根據(jù)文件的實際情況,輸出結(jié)果可能不同)
# /etc/hosts 文件可讀
# /etc/hosts 文件可寫
# /etc/hosts 文件不可執(zhí)行
# /etc/hosts 文件為普通文件
# /etc/hosts 文件不是個目錄
# /etc/hosts 文件不為空
# /etc/hosts 文件存在
控制語句
條件語句
跟其它程序設(shè)計語言一樣,Bash 中的條件語句讓我們可以決定一個操作是否被執(zhí)行。結(jié)果取決于一個包在[[ ]]里的表達式。
由[[ ]](sh 中是[ ])包起來的表達式被稱作 檢測命令 或 基元。這些表達式幫助我們檢測一個條件的結(jié)果。這里可以找到有關(guān) bash 中單雙中括號區(qū)別的答案。
共有兩個不同的條件表達式:if 和 case。
if
(1)if 語句
if 在使用上跟其它語言相同。如果中括號里的表達式為真,那么 then 和 fi 之間的代碼會被執(zhí)行。fi 標(biāo)志著條件代碼塊的結(jié)束。
# 寫成一行
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi
# Output: 1 -eq 1 result is: true# 寫成多行
if [[ "abc" -eq "abc" ]]
thenecho ""abc" -eq "abc" result is: true"
fi
# Output: abc -eq abc result is: true
(2)if else 語句
同樣,我們可以使用 if…else 語句,例如:
if [[ 2 -ne 1 ]]; thenecho "true"
elseecho "false"
fi
# Output: true
(3)if elif else 語句
有些時候,if…else 不能滿足我們的要求。別忘了 if…elif…else,使用起來也很方便。
💻 “示例源碼”:
x=10
y=20
if [[ ${x} > ${y} ]]; thenecho "${x} > ${y}"
elif [[ ${x} < ${y} ]]; thenecho "${x} < ${y}"
elseecho "${x} = ${y}"
fi
# Output: 10 < 20
case
如果你需要面對很多情況,分別要采取不同的措施,那么使用 case 會比嵌套的 if 更有用。使用 case 來解決復(fù)雜的條件判斷,看起來像下面這樣:
💻 “示例源碼”:
exec
case ${oper} in"+")val=`expr ${x} + ${y}`echo "${x} + ${y} = ${val}";;"-")val=`expr ${x} - ${y}`echo "${x} - ${y} = ${val}";;"*")val=`expr ${x} \* ${y}`echo "${x} * ${y} = ${val}";;"/")val=`expr ${x} / ${y}`echo "${x} / ${y} = ${val}";;*)echo "Unknown oper!";;
esac
每種情況都是匹配了某個模式的表達式。|用來分割多個模式,)用來結(jié)束一個模式序列。第一個匹配上的模式對應(yīng)的命令將會被執(zhí)行。*代表任何不匹配以上給定模式的模式。命令塊兒之間要用;;分隔。
循環(huán)語句
循環(huán)其實不足為奇。跟其它程序設(shè)計語言一樣,bash 中的循環(huán)也是只要控制條件為真就一直迭代執(zhí)行的代碼塊。
Bash 中有四種循環(huán):
- for
- while
- until
- select
for 循環(huán)
for 與它在 C 語言中的姊妹非常像??雌饋硎沁@樣:
for arg in elem1 elem2 ... elemN
do### 語句
done
在每次循環(huán)的過程中,arg 依次被賦值為從 elem1 到 elemN。這些值還可以是通配符或者大括號擴展。
當(dāng)然,我們還可以把 for 循環(huán)寫在一行,但這要求 do 之前要有一個分號,就像下面這樣:
for i in {1..5}; do echo $i; done
還有,如果你覺得 for…in…do 對你來說有點奇怪,那么你也可以像 C 語言那樣使用 for,比如:
for (( i = 0; i < 10; i++ )); doecho $i
done
當(dāng)我們想對一個目錄下的所有文件做同樣的操作時,for 就很方便了。舉個例子,如果我們想把所有的.bash 文件移動到 script 文件夾中,并給它們可執(zhí)行權(quán)限,我們的腳本可以這樣寫:
💻 “示例源碼”:
DIR=/home/zp
for FILE in ${DIR}/*.sh; domv "$FILE" "${DIR}/scripts"
done
# 將 /home/zp 目錄下所有 sh 文件拷貝到 /home/zp/scripts
while 循環(huán)
while 循環(huán)檢測一個條件,只要這個條件為 真,就執(zhí)行一段命令。被檢測的條件跟 if…then 中使用的基元并無二異。因此一個 while 循環(huán)看起來會是這樣:
while [[ condition ]]
do### 語句
done
跟 for 循環(huán)一樣,如果我們把 do 和被檢測的條件寫到一行,那么必須要在 do 之前加一個分號。
💻 “示例源碼”:
### 0到9之間每個數(shù)的平方
x=0
while [[ ${x} -lt 10 ]]; doecho $((x * x))x=$((x + 1))
done
# Output:
# 0
# 1
# 4
# 9
# 16
# 25
# 36
# 49
# 64
# 81
until 循環(huán)
until 循環(huán)跟 while 循環(huán)正好相反。它跟 while 一樣也需要檢測一個測試條件,但不同的是,只要該條件為 假 就一直執(zhí)行循環(huán):
💻 “示例源碼”:
x=0
until [[ ${x} -ge 5 ]]; doecho ${x}x=`expr ${x} + 1`
done
# Output:
# 0
# 1
# 2
# 3
# 4
select 循環(huán)
select 循環(huán)幫助我們組織一個用戶菜單。它的語法幾乎跟 for 循環(huán)一致:
select answer in elem1 elem2 ... elemN
do### 語句
done
select 會打印 elem1…elemN 以及它們的序列號到屏幕上,之后會提示用戶輸入。通??吹降氖?$?(PS3 變量)。用戶的選擇結(jié)果會被保存到 answer 中。如果 answer 是一個在 1…N 之間的數(shù)字,那么語句會被執(zhí)行,緊接著會進行下一次迭代 —— 如果不想這樣的話我們可以使用 break 語句。
💻 “示例源碼”:
#!/usr/bin/env bashPS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case ${ITEM} inbower) bower install ${PACKAGE} ;;npm) npm install ${PACKAGE} ;;gem) gem install ${PACKAGE} ;;pip) pip install ${PACKAGE} ;;
esac
break # 避免無限循環(huán)
done
這個例子,先詢問用戶他想使用什么包管理器。接著,又詢問了想安裝什么包,最后執(zhí)行安裝操作。
運行這個腳本,會得到如下輸出:
$ ./my_script
1) bower
2) npm
3) gem
4) pip
Choose the package manager: 2
Enter the package name: gitbook-cli
break 和 continue
如果想提前結(jié)束一個循環(huán)或跳過某次循環(huán)執(zhí)行,可以使用 shell 的 break 和 continue 語句來實現(xiàn)。它們可以在任何循環(huán)中使用。
break 語句用來提前結(jié)束當(dāng)前循環(huán)。
continue 語句用來跳過某次迭代。
💻 “示例源碼”:
# 查找 10 以內(nèi)第一個能整除 2 和 3 的正整數(shù)
i=1
while [[ ${i} -lt 10 ]]; doif [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; thenecho ${i}break;fii=`expr ${i} + 1`
done
# Output: 6
💻 “示例源碼”:
# 打印10以內(nèi)的奇數(shù)
for (( i = 0; i < 10; i ++ )); doif [[ $((i % 2)) -eq 0 ]]; thencontinue;fiecho ${i}
done
# Output:
# 1
# 3
# 5
# 7
# 9
函數(shù)
bash 函數(shù)定義語法如下:
[ function ] funname [()] {action;[return int;]
}
💡 說明:
函數(shù)定義時,function 關(guān)鍵字可有可無。
函數(shù)返回值 - return 返回函數(shù)返回值,返回值類型只能為整數(shù)(0-255)。如果不加 return 語句,shell 默認將以最后一條命令的運行結(jié)果,作為函數(shù)返回值。
函數(shù)返回值在調(diào)用該函數(shù)后通過 $? 來獲得。
所有函數(shù)在使用前必須定義。這意味著必須將函數(shù)放在腳本開始部分,直至 shell 解釋器首次發(fā)現(xiàn)它時,才可以使用。調(diào)用函數(shù)僅使用其函數(shù)名即可。
💻 “示例源碼”:
#!/usr/bin/env bashcalc(){PS3="choose the oper: "select oper in + - \* / # 生成操作符選擇菜單doecho -n "enter first num: " && read x # 讀取輸入?yún)?shù)echo -n "enter second num: " && read y # 讀取輸入?yún)?shù)execcase ${oper} in"+")return $((${x} + ${y}));;"-")return $((${x} - ${y}));;"*")return $((${x} * ${y}));;"/")return $((${x} / ${y}));;*)echo "${oper} is not support!"return 0;;esacbreakdone
}
calc
echo "the result is: $?" # $? 獲取 calc 函數(shù)返回值
執(zhí)行結(jié)果:
$ ./function-demo.sh
1) +
2) -
3) *
4) /
choose the oper: 3
enter first num: 10
enter second num: 10
the result is: 100
位置參數(shù)
位置參數(shù)是在調(diào)用一個函數(shù)并傳給它參數(shù)時創(chuàng)建的變量。
位置參數(shù)變量表:
變量 | 描述 |
---|---|
$0 | 腳本名稱 |
$1 … $9 | 第 1 個到第 9 個參數(shù)列表 |
${10} … ${N} | 第 10 個到 N 個參數(shù)列表(N 是一個正整數(shù)) |
$* 或 $@ | 除了 $0 外的所有位置參數(shù) |
$# | 不包括 $0 在內(nèi)的位置參數(shù)的個數(shù) |
$FUNCNAME | 函數(shù)名稱(僅在函數(shù)內(nèi)部有值) |
💻 “示例源碼”:
#!/usr/bin/env bashx=0
if [[ -n $1 ]]; thenecho "第一個參數(shù)為:$1"x=$1
elseecho "第一個參數(shù)為空"
fiy=0
if [[ -n $2 ]]; thenecho "第二個參數(shù)為:$2"y=$2
elseecho "第二個參數(shù)為空"
fiparamsFunction(){echo "函數(shù)第一個入?yún)?#xff1a;$1"echo "函數(shù)第二個入?yún)?#xff1a;$2"
}
paramsFunction ${x} ${y}
執(zhí)行結(jié)果:
$ ./function-demo2.sh
第一個參數(shù)為空
第二個參數(shù)為空
函數(shù)第一個入?yún)?#xff1a;0
函數(shù)第二個入?yún)?#xff1a;0$ ./function-demo2.sh 10 20
第一個參數(shù)為:10
第二個參數(shù)為:20
函數(shù)第一個入?yún)?#xff1a;10
函數(shù)第二個入?yún)?#xff1a;20
執(zhí)行 ./variable-demo4.sh hello world
,然后在腳本中通過 $1
、$2
… 讀取第 1 個參數(shù)、第 2 個參數(shù)。。。
函數(shù)處理參數(shù)
另外,還有幾個特殊字符用來處理參數(shù):
參數(shù)處理 | 說明 |
---|---|
$# | 返回參數(shù)個數(shù) |
$* | 返回所有參數(shù) |
$$ | 腳本運行的當(dāng)前進程 ID 號 |
$! | 后臺運行的最后一個進程的 ID 號 |
$@ | 返回所有參數(shù)(與 $* 相同) |
$- | 返回 Shell 使用的當(dāng)前選項,與 set 命令功能相同。 |
$? | 函數(shù)返回值(或上一個命令的退出狀態(tài)) |
💻 “示例源碼”:
runner() {return 0
}name=zp
paramsFunction(){echo "函數(shù)第一個入?yún)?#xff1a;$1"echo "函數(shù)第二個入?yún)?#xff1a;$2"echo "傳遞到腳本的參數(shù)個數(shù):$#"echo "所有參數(shù):"printf "+ %s\n" "$*"echo "腳本運行的當(dāng)前進程 ID 號:$$"echo "后臺運行的最后一個進程的 ID 號:$!"echo "所有參數(shù):"printf "+ %s\n" "$@"echo "Shell 使用的當(dāng)前選項:$-"runnerecho "runner 函數(shù)的返回值:$?"
}
paramsFunction 1 "abc" "hello, \"zp\""
# Output:
# 函數(shù)第一個入?yún)?#xff1a;1
# 函數(shù)第二個入?yún)?#xff1a;abc
# 傳遞到腳本的參數(shù)個數(shù):3
# 所有參數(shù):
# + 1 abc hello, "zp"
# 腳本運行的當(dāng)前進程 ID 號:26400
# 后臺運行的最后一個進程的 ID 號:
# 所有參數(shù):
# + 1
# + abc
# + hello, "zp"
# Shell 使用的當(dāng)前選項:hB
# runner 函數(shù)的返回值:0
Shell 擴展
擴展 發(fā)生在一行命令被分成一個個的 記號(tokens) 之后。換言之,擴展是一種執(zhí)行數(shù)學(xué)運算的機制,還可以用來保存命令的執(zhí)行結(jié)果,等等。
感興趣的話可以閱讀關(guān)于 shell 擴展的更多細節(jié)。
大括號擴展
大括號擴展讓生成任意的字符串成為可能。它跟 文件名擴展 很類似,舉個例子:
echo beg{i,a,u}n ### begin began begun
大括號擴展還可以用來創(chuàng)建一個可被循環(huán)迭代的區(qū)間。
echo {0..5} ### 0 1 2 3 4 5
echo {00..8..2} ### 00 02 04 06 08
命令置換
命令置換允許我們對一個命令求值,并將其值置換到另一個命令或者變量賦值表達式中。當(dāng)一個命令被``或 $()包圍時,命令置換將會執(zhí)行。舉個例子:
now=`date +%T`
### or
now=$(date +%T)echo $now ### 19:08:26
算數(shù)擴展
在 bash 中,執(zhí)行算數(shù)運算是非常方便的。算數(shù)表達式必須包在 $(( ))中。算數(shù)擴展的格式為:
result=$(( ((10 + 5*3) - 7) / 2 ))
echo $result ### 9
在算數(shù)表達式中,使用變量無需帶上 $ 前綴:
x=4
y=7
echo $(( x + y )) ### 11
echo $(( ++x + y++ )) ### 12
echo $(( x + y )) ### 13
單引號和雙引號
單引號和雙引號之間有很重要的區(qū)別。在雙引號中,變量引用或者命令置換是會被展開的。在單引號中是不會的。舉個例子:
echo "Your home: $HOME" ### Your home: /Users/<username>
echo 'Your home: $HOME' ### Your home: $HOME
當(dāng)局部變量和環(huán)境變量包含空格時,它們在引號中的擴展要格外注意。隨便舉個例子,假如我們用 echo 來輸出用戶的輸入:
INPUT="A string with strange whitespace."
echo $INPUT ### A string with strange whitespace.
echo "$INPUT" ### A string with strange whitespace.
調(diào)用第一個 echo 時給了它 5 個單獨的參數(shù) —— $INPUT
被分成了單獨的詞,echo 在每個詞之間打印了一個空格。第二種情況,調(diào)用 echo 時只給了它一個參數(shù)(整個 $INPUT
的值,包括其中的空格)。
來看一個更嚴肅的例子:
FILE="Favorite Things.txt"
cat $FILE ### 嘗試輸出兩個文件: `Favorite` 和 `Things.txt`
cat "$FILE" ### 輸出一個文件: `Favorite Things.txt`
盡管這個問題可以通過把 FILE 重命名成 Favorite-Things.txt 來解決,但是,假如這個值來自某個環(huán)境變量,來自一個位置參數(shù),或者來自其它命令(find, cat, 等等)呢。因此,如果輸入 可能 包含空格,務(wù)必要用引號把表達式包起來。
流和重定向
Bash 有很強大的工具來處理程序之間的協(xié)同工作。使用流,我們能將一個程序的輸出發(fā)送到另一個程序或文件,因此,我們能方便地記錄日志或做一些其它我們想做的事。
管道給了我們創(chuàng)建傳送帶的機會,控制程序的執(zhí)行成為可能。
學(xué)習(xí)如何使用這些強大的、高級的工具是非常非常重要的。
輸入、輸出流
Bash 接收輸入,并以字符序列或 字符流 的形式產(chǎn)生輸出。這些流能被重定向到文件或另一個流中。
有三個文件描述符:
代碼 | 描述符 | 描述 |
---|---|---|
0 | stdin | 標(biāo)準輸入 |
1 | stdout | 標(biāo)準輸出 |
2 | stderr | 標(biāo)準錯誤輸出 |
重定向
重定向讓我們可以控制一個命令的輸入來自哪里,輸出結(jié)果到什么地方。這些運算符在控制流的重定向時會被用到:
Operator | Description |
---|---|
> | 重定向輸出 |
&> | 重定向輸出和錯誤輸出 |
&>> | 以附加的形式重定向輸出和錯誤輸出 |
< | 重定向輸入 |
<< | Here 文檔語法 |
<<< | Here 字符串 |
以下是一些使用重定向的例子:
### ls的結(jié)果將會被寫到list.txt中
ls -l > list.txt### 將輸出附加到list.txt中
ls -a >> list.txt### 所有的錯誤信息會被寫到errors.txt中
grep da * 2> errors.txt### 從errors.txt中讀取輸入
less < errors.txt
/dev/null 文件
如果希望執(zhí)行某個命令,但又不希望在屏幕上顯示輸出結(jié)果,那么可以將輸出重定向到 /dev/null:
$ command > /dev/null
/dev/null 是一個特殊的文件,寫入到它的內(nèi)容都會被丟棄;如果嘗試從該文件讀取內(nèi)容,那么什么也讀不到。但是 /dev/null 文件非常有用,將命令的輸出重定向到它,會起到”禁止輸出”的效果。
如果希望屏蔽 stdout 和 stderr,可以這樣寫:
$ command > /dev/null 2>&1
Debug
shell 提供了用于 debug 腳本的工具。
如果想采用 debug 模式運行某腳本,可以在其 shebang 中使用一個特殊的選項:
#!/bin/bash options
options 是一些可以改變 shell 行為的選項。下表是一些可能對你有用的選項:
Short | Name | Description |
---|---|---|
-f | noglob | 禁止文件名展開(globbing) |
-i | interactive | 讓腳本以交互模式運行 |
-n | noexec | 讀取命令,但不執(zhí)行(語法檢查) |
-t | — | 執(zhí)行完第一條命令后退出 |
-v | verbose | 在執(zhí)行每條命令前,向 stderr 輸出該命令 |
-x | xtrace | 在執(zhí)行每條命令前,向 stderr 輸出該命令以及該命令的擴展參數(shù) |
舉個例子,如果我們在腳本中指定了 -x
例如:
#!/bin/bash -xfor (( i = 0; i < 3; i++ )); doecho $i
done
這會向 stdout 打印出變量的值和一些其它有用的信息:
$ ./my_script
+ (( i = 0 ))
+ (( i < 3 ))
+ echo 0
0
+ (( i++ ))
+ (( i < 3 ))
+ echo 1
1
+ (( i++ ))
+ (( i < 3 ))
+ echo 2
2
+ (( i++ ))
+ (( i < 3 ))
有時我們值需要 debug 腳本的一部分。這種情況下,使用 set 命令會很方便。這個命令可以啟用或禁用選項。使用-啟用選項,+禁用選項:
💻 “示例源碼”:
# 開啟 debug
set -x
for (( i = 0; i < 3; i++ )); doprintf ${i}
done
# 關(guān)閉 debug
set +x
# Output:
# + (( i = 0 ))
# + (( i < 3 ))
# + printf 0
# 0+ (( i++ ))
# + (( i < 3 ))
# + printf 1
# 1+ (( i++ ))
# + (( i < 3 ))
# + printf 2
# 2+ (( i++ ))
# + (( i < 3 ))
# + set +xfor i in {1..5}; do printf ${i}; done
printf "\n"
# Output: 12345
往期精彩文章
Oracle 一鍵巡檢自動生成 Word 報告
Oracle 一鍵安裝合集
Oracle一鍵安裝腳本的 21 個疑問與解答
Oracle一鍵巡檢腳本的 21 個疑問與解答
全網(wǎng)首發(fā):Oracle 23ai 一鍵安裝腳本(非 RPM)
Oracle 19C 最新 RU 補丁 19.24 ,一鍵安裝!
Oracle Linux 7.9 一鍵安裝 Oracle 19C
RedHat 9.4(aarch64) 一鍵安裝 Oracle 19C
openEuler 22.03 LTS SP4 一鍵安裝 Oracle 19C RAC
RHEL 7.9 一鍵安裝 Oracle 19C 19.23 RAC
Oracle DataGuard GAP 修復(fù)手冊
優(yōu)化 Oracle:最佳實踐與開發(fā)規(guī)范
DBA 必備:Linux 軟件源配置全攻略
Linux 一鍵配置時鐘同步全攻略
感謝您的閱讀,這里是 Lucifer三思而后行,歡迎點贊+關(guān)注,我會持續(xù)分享數(shù)據(jù)庫知識、運維技巧。