shell相關(guān)4

j腳本除錯

如何考慮命令出錯的情況锣吼。
比如:

#! /bin/bash

dir_name=/path/not/exist

cd $dir_name
rm *

上面腳本中,如果目錄$dir_name不存在古徒,cd $dir_name命令就會執(zhí)行失敗读恃。如果$dir_name為空,則會進入主目錄刪除所有文件疹吃。
以下寫法才是正確:

[[ -d $dir_name ]] && cd $dir_name && rm *

如果不放心刪除什么文件西雀,可以先打印出來看一下:

[[ -d $dir_name ]] && cd $dir_name && echo rm *

上面命令中艇肴,echo rm *不會刪除文件,只會打印出來要刪除的文件核畴。

bash的-x參數(shù)可以在執(zhí)行每一行命令之前冲九,打印該命令:
比如:

# script.sh
echo hello world

加上參數(shù)-x之后的執(zhí)行結(jié)果如下:

$ bash -x script.sh
+ echo hello world
hello world

也可以將-x寫在Shebang行中:

#! /bin/bash -x
# trouble: script to demonstrate common errors

number=1
if [ $number = 1 ]; then
  echo "Number is equal to 1."
else
  echo "Number is not equal to 1."
fi

執(zhí)行結(jié)果如下:

$ trouble
+ number=1
+ '[' 1 = 1 ']'
+ echo 'Number is equal to 1.'
Number is equal to 1.

輸出的命令之前的+號,是由系統(tǒng)變量PS4決定,可以修改這個變量:

$ export PS4='$LINENO + '
$ trouble
5 + number=1
7 + '[' 1 = 1 ']'
8 + echo 'Number is equal to 1.'
Number is equal to 1.

環(huán)境變量:
變量$LINENO返回它在腳本里面的行號:

#!/bin/bash

echo "This is line $LINENO"

運行結(jié)果如下:

$ ./test.sh
This is line 3

變量$FUNCNAME返回一個數(shù)組憾筏,內(nèi)容是當(dāng)前的函數(shù)調(diào)用堆棧花鹅。該數(shù)組的0號成員是當(dāng)前調(diào)用的函數(shù),1號成員是調(diào)用當(dāng)前函數(shù)的函數(shù)古拴,以此類推:

function func1()
{
  echo "func1: FUNCNAME0 is ${FUNCNAME[0]}"
  echo "func1: FUNCNAME1 is ${FUNCNAME[1]}"
  echo "func1: FUNCNAME2 is ${FUNCNAME[2]}"
  func2
}

function func2()
{
  echo "func2: FUNCNAME0 is ${FUNCNAME[0]}"
  echo "func2: FUNCNAME1 is ${FUNCNAME[1]}"
  echo "func2: FUNCNAME2 is ${FUNCNAME[2]}"
}

func1

運行結(jié)果如下:

$ ./test.sh
func1: FUNCNAME0 is func1
func1: FUNCNAME1 is main
func1: FUNCNAME2 is
func2: FUNCNAME0 is func2
func2: FUNCNAME1 is func1
func2: FUNCNAME2 is main

可以發(fā)現(xiàn)黄痪,有一個main函數(shù)盔然。

變量$BASH_SOURCE返回一個數(shù)組是嗜,內(nèi)容是當(dāng)前的腳本調(diào)用堆棧挺尾。該數(shù)組的0號成員是當(dāng)前執(zhí)行的腳本,1號成員是調(diào)用當(dāng)前腳本的腳本丽柿,以此類推魂挂,跟變量$FUNCNAME是一一對應(yīng)關(guān)系锰蓬。
下面有兩個子腳本lib1.sh和lib2.sh:

# lib1.sh
function func1()
{
  echo "func1: BASH_SOURCE0 is ${BASH_SOURCE[0]}"
  echo "func1: BASH_SOURCE1 is ${BASH_SOURCE[1]}"
  echo "func1: BASH_SOURCE2 is ${BASH_SOURCE[2]}"
  func2
}
# lib2.sh
function func2()
{
  echo "func2: BASH_SOURCE0 is ${BASH_SOURCE[0]}"
  echo "func2: BASH_SOURCE1 is ${BASH_SOURCE[1]}"
  echo "func2: BASH_SOURCE2 is ${BASH_SOURCE[2]}"
}

然后,主腳本main.sh調(diào)用上面兩個子腳本:

#!/bin/bash
# main.sh

source lib1.sh
source lib2.sh

func1

執(zhí)行主腳本main.sh麻顶,會得到下面的結(jié)果:

$ ./main.sh
func1: BASH_SOURCE0 is lib1.sh
func1: BASH_SOURCE1 is ./main.sh
func1: BASH_SOURCE2 is
func2: BASH_SOURCE0 is lib2.sh
func2: BASH_SOURCE1 is lib1.sh
func2: BASH_SOURCE2 is ./main.sh

變量$BASH_LINENO返回一個數(shù)組舱卡,內(nèi)容是每一輪調(diào)用對應(yīng)的行號。${BASH_LINENO[$i]}${FUNCNAME[$i]}是一一對應(yīng)關(guān)系矫钓,表示${FUNCNAME[$i]}在調(diào)用它的腳本文件${BASH_SOURCE[$i+1]}里面的行號舍杜。
下面有兩個子腳本lib1.sh和lib2.sh:

# lib1.sh
function func1()
{
  echo "func1: BASH_LINENO is ${BASH_LINENO[0]}"
  echo "func1: FUNCNAME is ${FUNCNAME[0]}"
  echo "func1: BASH_SOURCE is ${BASH_SOURCE[1]}"

  func2
}
# lib2.sh
function func2()
{
  echo "func2: BASH_LINENO is ${BASH_LINENO[0]}"
  echo "func2: FUNCNAME is ${FUNCNAME[0]}"
  echo "func2: BASH_SOURCE is ${BASH_SOURCE[1]}"
}

然后既绩,主腳本main.sh調(diào)用上面兩個子腳本:

#!/bin/bash
# main.sh

source lib1.sh
source lib2.sh

func1

執(zhí)行主腳本main.sh,會得到下面的結(jié)果:

$ ./main.sh
func1: BASH_LINENO is 7
func1: FUNCNAME is func1
func1: BASH_SOURCE is main.sh
func2: BASH_LINENO is 8
func2: FUNCNAME is func2
func2: BASH_SOURCE is lib1.sh

上面例子中私杜,函數(shù)func1是在main.sh的第7行調(diào)用救欧,函數(shù)func2是在lib1.sh的第8行調(diào)用的。

mktemp 命令铝耻,trap 命令

Bash 腳本有時需要創(chuàng)建臨時文件或臨時目錄蹬刷。常見的做法是替废,在/tmp目錄里面創(chuàng)建文件或目錄椎镣,這樣做有很多弊端兽赁,使用mktemp命令是最安全的做法。
/tmp目錄是所有人可讀寫的惊科,任何用戶都可以往該目錄里面寫文件亮钦。創(chuàng)建的臨時文件也是所有人可讀的:

$ touch /tmp/info.txt
$ ls -l /tmp/info.txt
-rw-r--r-- 1 ruanyf ruanyf 0 12月 28 17:12 /tmp/info.txt

因此,臨時文件最好使用不可預(yù)測蜡娶、每次都不一樣的文件名映穗,防止被利用。臨時文件使用完畢宿接,應(yīng)該刪除辕录。但是,腳本意外退出時碎赢,往往會忽略清理臨時文件速梗。
生成臨時文件應(yīng)該遵循下面的規(guī)則:

創(chuàng)建前檢查文件是否已經(jīng)存在襟齿。
確保臨時文件已成功創(chuàng)建猜欺。
臨時文件必須有權(quán)限的限制。
臨時文件要使用不可預(yù)測的文件名涧黄。
腳本退出時,要刪除臨時文件(使用trap命令)笋妥。

mktemp命令就是為安全創(chuàng)建臨時文件而設(shè)計的春宣。雖然在創(chuàng)建臨時文件之前,它不會檢查臨時文件是否存在躏惋,但是它支持唯一文件名和清除機制嚷辅,因此可以減輕安全攻擊的風(fēng)險。
直接運行mktemp命令扁位,就能生成一個臨時文件:

$ mktemp
/tmp/tmp.4GcsWSG4vj

$ ls -l /tmp/tmp.4GcsWSG4vj
-rw------- 1 ruanyf ruanyf 0 12月 28 12:49 /tmp/tmp.4GcsWSG4vj

Bash 腳本使用mktemp命令的用法如下:

#!/bin/bash

TMPFILE=$(mktemp)
echo "Our temp file is $TMPFILE"

為了確保臨時文件創(chuàng)建成功攘乒,mktemp命令后面最好使用 OR 運算符||则酝,保證創(chuàng)建失敗時退出腳本:

#!/bin/bash

TMPFILE=$(mktemp) || exit 1
echo "Our temp file is $TMPFILE"

為了保證腳本退出時臨時文件被刪除,可以使用trap命令指定退出時的清除操作:

#!/bin/bash

trap 'rm -f "$TMPFILE"' EXIT

TMPFILE=$(mktemp) || exit 1
echo "Our temp file is $TMPFILE"

mktemp 命令的參數(shù):
-d:可以創(chuàng)建一個臨時目錄:

$ mktemp -d
/tmp/tmp.Wcau5UjmN6

-p:可以指定臨時文件所在的目錄般卑。默認是使用$TMPDIR環(huán)境變量指定的目錄爽雄,如果這個變量沒設(shè)置,那么使用/tmp目錄:

$ mktemp -p /home/ruanyf/
/home/ruanyf/tmp.FOKEtvs2H3

-t:可以指定臨時文件的文件名模板叹谁,模板的末尾必須至少包含三個連續(xù)的X字符乘盖,表示隨機字符,建議至少使用六個X析苫。默認的文件名模板是tmp.后接十個隨機字符:

$ mktemp -t mytemp.XXXXXXX
/tmp/mytemp.yZ1HgZV

trap命令:
trap命令用來在 Bash 腳本中響應(yīng)系統(tǒng)信號衩侥。
最常見的系統(tǒng)信號就是 SIGINT(中斷),即按 Ctrl + C 所產(chǎn)生的信號茫死。trap命令的-l參數(shù)璧榄,可以列出所有的系統(tǒng)信號:

$ trap -l

trap的命令格式如下:

$ trap [動作] [信號1] [信號2] ...

上面代碼中,“動作”是一個 Bash 命令涂身,“信號”常用的有以下幾個:

HUP:編號1搓蚪,腳本與所在的終端脫離聯(lián)系。
INT:編號2悴能,用戶按下 Ctrl + C雳灾,意圖讓腳本終止運行。
QUIT:編號3炒嘲,用戶按下 Ctrl + 斜杠匈庭,意圖退出腳本阱持。
KILL:編號9,該信號用于殺死進程衷咽。
TERM:編號15镶骗,這是kill命令發(fā)出的默認信號。
EXIT:編號0,這不是系統(tǒng)信號,而是 Bash 腳本特有的信號即横,不管什么情況裆赵,只要退出腳本就會產(chǎn)生战授。

trap命令響應(yīng)EXIT信號的寫法如下:

$ trap 'rm -f "$TMPFILE"' EXIT

上面命令中,腳本遇到EXIT信號時份帐,就會執(zhí)行rm -f "$TMPFILE"楣导。

trap 命令的常見使用場景,就是在 Bash 腳本中指定退出時執(zhí)行的清理命令:

#!/bin/bash

trap 'rm -f "$TMPFILE"' EXIT

TMPFILE=$(mktemp) || exit 1
ls /etc > $TMPFILE
if grep -qi "kernel" $TMPFILE; then
  echo 'find'
fi

上面代碼中噩凹,不管是腳本正常執(zhí)行結(jié)束毡咏,還是用戶按 Ctrl + C 終止,都會產(chǎn)生EXIT信號堵泽,從而觸發(fā)刪除臨時文件臊旭。
注意离熏,trap命令必須放在腳本的開頭。否則钻蔑,它上方的任何命令導(dǎo)致腳本退出奸鸯,都不會被它捕獲。

如果trap需要觸發(fā)多條命令窗怒,可以封裝一個 Bash 函數(shù):

function egress {
  command1
  command2
  command3
}

trap egress EXIT

登錄session&非登錄session

登錄session:
登錄 Session 是用戶登錄系統(tǒng)以后,系統(tǒng)為用戶開啟的原始 Session努隙,通常需要用戶輸入用戶名和密碼進行登錄辜昵。
登錄 Session 一般進行整個系統(tǒng)環(huán)境的初始化堪置,啟動的初始化腳本依次如下:

/etc/profile:所有用戶的全局配置腳本。
/etc/profile.d目錄里面所有.sh文件
~/.bash_profile:用戶的個人配置腳本岭洲。如果該腳本存在雁竞,則執(zhí)行完就不再往下執(zhí)行。
~/.bash_login:如果~/.bash_profile沒找到彪腔,則嘗試執(zhí)行這個腳本(C shell 的初始化腳本)进栽。如果該腳本存在,則執(zhí)行完就不再往下執(zhí)行格嗅。
~/.profile:如果~/.bash_profile和~/.bash_login都沒找到屯掖,則嘗試讀取這個腳本(Bourne shell 和 Korn shell 的初始化腳本)襟衰。

下面是一個典型的.bash_profile文件:

# .bash_profile
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
PATH=$PATH:$HOME/bin

SHELL=/bin/bash
MANPATH=/usr/man:/usr/X11/man
EDITOR=/usr/bin/vi
PS1='\h:\w\$ '
PS2='> '

if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

export PATH
export EDITOR

非登錄session:
非登錄 Session 是用戶進入系統(tǒng)以后瀑晒,手動新建的 Session,這時不會進行環(huán)境初始化苔悦。比如玖详,在命令行執(zhí)行bash命令勤讽,就會新建一個非登錄 Session拗踢。
非登錄 Session 的初始化腳本依次如下:

/etc/bash.bashrc:對全體用戶有效秒拔。
~/.bashrc:僅對當(dāng)前用戶有效飒硅。

注意三娩,執(zhí)行腳本相當(dāng)于新建一個非互動的 Bash 環(huán)境,但是這種情況不會調(diào)用~/.bashrc雀监。

~/.bash_logout腳本在每次退出 Session 時執(zhí)行会前,通常用來做一些清理工作和記錄工作,比如刪除臨時文件蔚万,記錄用戶在本次 Session 花費的時間临庇。

為了方便 Debug假夺,有時在啟動 Bash 的時候,可以加上啟動參數(shù):

-n:不運行腳本梧田,只檢查是否有語法錯誤悼尾。
-v:輸出每一行語句運行結(jié)果前,會先輸出該行語句未状。
-x:每一個命令處理之前司草,先輸出該命令,再執(zhí)行該命令猜憎。
$ bash -n scriptname
$ bash -v scriptname
$ bash -x scriptname

命令提示符

用戶進入 Bash 以后搔课,Bash 會顯示一個命令提示符,用來提示用戶在該位置后面輸入命令柬讨。
命令提示符通常是美元符號$袍啡,對于根用戶則是井號#境输。這個符號是環(huán)境變量PS1決定的,執(zhí)行下面的命令辩越,可以看到當(dāng)前命令提示符的定義:

$ echo $PS1

Bash 允許用戶自定義命令提示符区匣,只要改寫這個變量即可蒋院。改寫后的PS1,可以放在用戶的 Bash 配置文件.bashrc里面姑丑,以后新建 Bash 對話時辞友,新的提示符就會生效。要在當(dāng)前窗口看到修改后的提示符称龙,可以執(zhí)行下面的命令:

$ source ~/.bashrc

除了PS1鲫尊,Bash 還提供了提示符相關(guān)的另外三個環(huán)境變量。
環(huán)境變量PS2是命令行折行輸入時系統(tǒng)的提示符咳蔚,默認為>

$ echo "hello
> world"

環(huán)境變量PS3是使用select命令時谈火,系統(tǒng)輸入菜單的提示符:
環(huán)境變量PS4默認為+。它是使用 Bash 的-x參數(shù)執(zhí)行腳本時扔字,每一行命令在執(zhí)行前都會先打印出來温技,并且在行首出現(xiàn)的那個提示符荒揣。
比如下面是腳本test.sh:

#!/bin/bash

echo "hello world"

使用-x參數(shù)執(zhí)行這個腳本:

$ bash -x test.sh
+ echo 'hello world'
hello world

參考:https://wangdoc.com/bash/prompt.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市焊刹,隨后出現(xiàn)的幾起案子系任,更是在濱河造成了極大的恐慌,老刑警劉巖虐块,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俩滥,死亡現(xiàn)場離奇詭異,居然都是意外死亡贺奠,警方通過查閱死者的電腦和手機霜旧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來儡率,“玉大人,你說我怎么就攤上這事儿普∑樘樱” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵眉孩,是天一觀的道長个绍。 經(jīng)常有香客問我,道長浪汪,這世上最難降的妖魔是什么巴柿? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮死遭,結(jié)果婚禮上广恢,老公的妹妹穿的比我還像新娘。我一直安慰自己呀潭,他們只是感情好袁波,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布瓦阐。 她就那樣靜靜地躺著,像睡著了一般篷牌。 火紅的嫁衣襯著肌膚如雪睡蟋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天枷颊,我揣著相機與錄音戳杀,去河邊找鬼。 笑死夭苗,一個胖子當(dāng)著我的面吹牛信卡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播题造,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼傍菇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了界赔?” 一聲冷哼從身側(cè)響起丢习,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淮悼,沒想到半個月后咐低,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡袜腥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年见擦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羹令。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲤屡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出福侈,到底是詐尸還是另有隱情执俩,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布癌刽,位于F島的核電站役首,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏显拜。R本人自食惡果不足惜衡奥,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望远荠。 院中可真熱鬧矮固,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至守伸,卻和暖如春绎秒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尼摹。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工见芹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蠢涝。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓玄呛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親和二。 傳聞我的和親對象是個殘疾皇子徘铝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容