概述
首先侠坎,咱們來了解一下蚁趁,什么是Shell
。操作系統(tǒng)內(nèi)核給我們提供了各種接口实胸,同時(shí)也提供了各種用戶層的庫他嫡,理論上我們基于這些可以編寫程序?qū)崿F(xiàn)各種我們想要的功能,不過問題是庐完,咱們不可能做什么事情都要重新編寫程序钢属,這樣使用起來也太困難了。因此门躯,操作系統(tǒng)(包括Linux)通常都會(huì)引入一個(gè)Shell
這樣的特殊程序淆党,這個(gè)程序會(huì)接受輸入的命令然后執(zhí)行,并可能將執(zhí)行結(jié)果呈現(xiàn)出來讶凉∪疚冢總結(jié)來說,Shell
是一個(gè)從輸入設(shè)備或者文件讀取命令懂讯,并且解釋荷憋、執(zhí)行的用戶態(tài)程序。
在Linux系統(tǒng)中褐望,通常使用的Shell
程序包括有:
- Sh (Bourne Shell)
- Bash (Bourne Again Shell)
- Csh (C Shell)
- Ksh (Korn Shell)
一般來說勒庄,Bash
應(yīng)該是使用最多的Shell
程序了,本文也主要基于Bash
來展開瘫里。
Shell展開(Shell Expansion)
Shell
程序是一個(gè)命令解釋器实蔽,因此在終端輸入命令之后,Shell
將掃描命令并做適當(dāng)?shù)男薷募跣@個(gè)過程稱為Shell展開盐须。Shell展開是Shell解釋執(zhí)行之前極為重要的一步玩荠,了解它將有利于你對Shell命令或者腳本的理解漆腌,本章節(jié)將逐步帶大家來了解這個(gè)過程。
命令參數(shù)解析
這里的空格包括了制表符(Tab)阶冈。當(dāng)Shell程序掃描輸入的命令時(shí)闷尿,會(huì)以連續(xù)的空格為界,將命令切分成一組參數(shù)女坑,因此你輸入多個(gè)空格為界跟輸入一個(gè)空格的效果是一樣的填具。通常來講,第一個(gè)參數(shù)就是要執(zhí)行的命令,而后面的參數(shù)則是改命令的參數(shù)劳景。一下幾個(gè)命令其實(shí)是等效的:
# echo Hello World
Hello World
# echo Hello World
Hello World
# echo Hello World
Hello World
引號
當(dāng)然誉简,有時(shí)候你需要在一個(gè)參數(shù)中包括空格,這樣的話你就需要將這個(gè)參數(shù)以引號引起來盟广,引號包括了單引號'
跟雙引號"
闷串,兩者都可以。shell
會(huì)將引號中的字符串視為一個(gè)參數(shù)筋量,不論里面有沒有空格烹吵。當(dāng)然,特別指出的是桨武,不要用反引號`
肋拔,反引號將在后面詳細(xì)講述。
如命令echo 'Hello World!'
在shell
解析之后會(huì)有兩個(gè)參數(shù)呀酸,分別為echo
跟Hello World!
凉蜂。而如果不用引號echo Hello World!
,則將解析為三個(gè)參數(shù)七咧。
特別提一下跃惫,對于
echo
命令,如果需要輸出需要轉(zhuǎn)義的字符艾栋,如回車等爆存,則需要執(zhí)行echo -e "Hello World!\n"
,如果不加-e
蝗砾,則\n
會(huì)被直接顯示出來先较。# echo "hello\n" hello\n # echo -e "hello\n" hello
命令
對于shell
來說,命令有內(nèi)部命令(Builtin Commands)跟外部命令(External Commands)之分悼粮,所謂內(nèi)部命令指的是包含在shell
解析器中的命令闲勺。內(nèi)部命令一般有4種類型:
-
sh
內(nèi)部命令這些內(nèi)部命令來源于
Bourne Shell
,通常包括了以下命令:
: . break cd continue eval exec exit export getopts hash pwd readonly return shift test/[ times trap umask unset
扣猫。 -
bash
內(nèi)部命令這些內(nèi)部命令來源于
Bourne Again Shell
菜循,通常包括了以下命令:
alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias
。 -
修改
shell
行為的內(nèi)部命令這些內(nèi)部命令用來修改
shell
的默認(rèn)行為申尤。包括了set shopt
命令癌幕。 -
特殊內(nèi)部命令
由于歷史原因,POSIX標(biāo)準(zhǔn)將一些內(nèi)部命令劃分為特殊內(nèi)部命令昧穿,特殊的之處在于這些命令的查找時(shí)間以及命令運(yùn)行后的狀態(tài)等方面勺远,只有當(dāng)Bash以POSIX模式運(yùn)行時(shí),這些命令才是特殊命令时鸵,否則它們跟其它內(nèi)部命令沒啥區(qū)別胶逢。特殊內(nèi)部命令包括了
break : . continue eval exec exit export readonly return set shift trap unset
。
內(nèi)部命令可能會(huì)被提前至于內(nèi)存中,因此運(yùn)行起來會(huì)比外部命令要快初坠。對于外部命令和簸,可以認(rèn)為除了內(nèi)部命令之后就可以認(rèn)為是外部命令了,通常來講碟刺,/bin
跟/sbin
下的都是外部命令比搭,當(dāng)然,應(yīng)用有關(guān)的通常也是外部命令南誊。
我們可以通過type
命令來查看一個(gè)命令是否是內(nèi)部命令:
# type cd
cd is a shell builtin
# type awk
awk is /usr/bin/awk
另外身诺,對于很多內(nèi)部命令,它們可能對應(yīng)的會(huì)有外部命令版本抄囚,可以通過type
命令來查看:
# type -a echo
echo is a shell builtin
echo is /usr/bin/echo
# type -a cd
cd is a shell builtin
cd is /usr/bin/cd
反過來霉赡,我們一般可以通過命令which
來查詢一個(gè)命令是否是外部命令:
# which awk
/usr/bin/awk
# which .
/usr/bin/which: no . in (/opt/rh/rh-python34/root/usr/bin:/usr/java/default/bin/:/usr/local/git/bin:/opt/ActiveTcl-8.5/bin:/root/perl5/bin:/root/env/maven/apache-maven-3.3.3/bin:/root/soft/wrk/wrk-4.0.1:/root/usr/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
總結(jié)一下,通過which
查詢出來的是其外部命令版本幔托,通過type
默認(rèn)查詢出來的是內(nèi)部命令:
# which echo
/usr/bin/echo
# type echo
echo is a shell builtin
對于內(nèi)部命令的詳細(xì)說明穴亏,可以查看GNU的文檔。
別名
可以用alias
命令給一個(gè)命令取一個(gè)別名:
# alias print=echo
# print "hello"
hello
# type print
print is aliased to `echo'
別名一個(gè)常用的用法是用來縮寫已知的命令:
# type ls
ls is aliased to `ls --color=auto'
可見ls
命令實(shí)際上是命令ls --color=auto
的別名重挑,這樣就相當(dāng)于改變了ls
命令的默認(rèn)行為了嗓化。
前面咱們通過type
命令來查看命令的別名,實(shí)際上更加推薦采用alias
或者which
來查看:
# alias ls
alias ls='ls --color=auto'
# which ls
alias ls='ls --color=auto'
/usr/bin/ls
如果要取消別名谬哀,則可以采用unalias
命令:
# which ls
alias ls='ls --color=auto'
/usr/bin/ls
# unalias ls
# which ls
/usr/bin/ls
顯示shell展開的結(jié)果
由于shell
展開的存在刺覆,你輸入的命令被展開之后可能會(huì)發(fā)生變化,如果需要知道shell
展開之后的命令史煎,可以使用內(nèi)部命令set
來修改shell
的默認(rèn)參數(shù)來顯示:
# set -x
++ printf '\033]0;%s@%s:%s\007' root traffic-base1 '~'
# echo hello world
+ echo hello world
hello world
++ printf '\033]0;%s@%s:%s\007' root traffic-base1 '~'
其中谦屑,以+
開頭的就是展開之后的命令,可見展開之后篇梭,shell
將多余的空格去掉了氢橙。如果不要再顯示了,可以輸入命令set +x
恬偷。
shell控制操作符 (Control Operators)
$?
操作符
每個(gè)命令執(zhí)行完后都會(huì)有個(gè)退出碼(Exit Code)悍手,其值為0時(shí)表示命令成功,否則命令失敗袍患。這個(gè)退出碼可以通過$?
來訪問坦康,執(zhí)行完命令后立馬訪問$?
可以獲取該命令的退出碼,并以此來判斷命令是否成功协怒。每個(gè)命令的執(zhí)行都會(huì)產(chǎn)生新的退出碼涝焙,所以請務(wù)必在命令執(zhí)行完卑笨,立刻訪問$?
來獲取退出碼孕暇。
初看起來,$?
似乎是一個(gè)shell
變量,但實(shí)際上并非如此妖滔,因?yàn)槟銦o法對$?
賦值隧哮。$?
準(zhǔn)確來說是shell
的一個(gè)內(nèi)部參數(shù)。
分號;
shell
命令輸入時(shí)座舍,你可以將多個(gè)命令輸入在一行沮翔,只要在不同命令之間以分號;
隔開,當(dāng)然分號不能是在引號中曲秉。
必須注意的是采蚀,如果將多個(gè)命令以
;
連接在一起,執(zhí)行的結(jié)果通過$?
查詢出來將只是最后一個(gè)命令的結(jié)果
&
符號
通常情況下承二,shell
會(huì)在前臺(tái)執(zhí)行命令榆鼠,并等待命令結(jié)束才返回。如果需要將命令放到后臺(tái)去執(zhí)行亥鸠,可以使用&
符號放在命令最后面妆够,這樣的話命令會(huì)被放在后臺(tái)執(zhí)行,shell
會(huì)立刻返回而不用等待命令結(jié)束负蚊。
注意的是神妹,即便放在后臺(tái)執(zhí)行,但是如果不處理好命令的輸入家妆,則命令的輸出可能會(huì)繼續(xù)在當(dāng)前的終端輸出鸵荠,后面會(huì)講述如何處理命令的輸出。
&&
操作符
此操作符表示邏輯與伤极,你可以將兩個(gè)命令用此操作符連接起來腰鬼,如cmd1 && cmd2
,只有當(dāng)cmd1
執(zhí)行成功之后塑荒,cmd2
才會(huì)被執(zhí)行熄赡。這里的成功指的是cmd1
的退出碼是0。
# hello && echo world
-bash: hello: command not found
# echo hello && echo world
hello
world
當(dāng)然齿税,&&
也可以將多個(gè)命令連接起來彼硫,其執(zhí)行類似,只有當(dāng)前面的命令成功凌箕,后面的才會(huì)執(zhí)行拧篮。因此,將多個(gè)命令寫在一行用&&
可以實(shí)現(xiàn)牵舱,只不過&&
必須按照邏輯與的關(guān)系執(zhí)行串绩,而;
號的話會(huì)執(zhí)行所有的命令。
||
操作符
很顯然芜壁,與&&
相對礁凡,||
操作符表示邏輯或的關(guān)系高氮,同樣可以連接兩個(gè)命令,如cmd1 || cmd2
顷牌,只有當(dāng)cmd1
失敗了剪芍,才會(huì)執(zhí)行cmd2
,這里的失敗指的是cmd1
的退出碼非0窟蓝。
&&
與||
混合
這兩個(gè)操作符是可以混合使用的罪裹,其遵循的原則保持一致,且是從左向右依次判斷运挫,結(jié)合這兩種操作符状共,可以實(shí)現(xiàn)類似于if then else
的邏輯結(jié)構(gòu)。如cmd1 && cmd2 || cmd3
意思就是如果cmd1
成功谁帕,則執(zhí)行cmd2
口芍,否則執(zhí)行cmd3
。但務(wù)必注意的是雇卷,此處并非真正意思上的if then else
邏輯淑掌,因?yàn)槿绻?code>cmd2也執(zhí)行失敗丑勤,cmd3
其實(shí)也會(huì)被執(zhí)行。如下例:
# echo hello && echo ok || echo world
hello
ok
# echo hello && rm dfsdf || echo world
hello
rm: cannot remove ‘dfsdf’: No such file or directory
world
&&
相當(dāng)于將兩條命令邏輯上連成了一條命令,這樣就變成了cmd1-2 || cmd3
钮呀,其中cmd1-2
就是cmd1 && cmd2
逗概,因此锹锰,cmd3
只要在cmd1-2
失敗的情況下都會(huì)被執(zhí)行拳亿,而cmd1-2
失敗的情況有兩種,一種是cmd1
失敗调榄,一種是cmd1
成功但是cmd2
失敗踊赠。同樣的,||
也會(huì)將兩條命令連成一條命令每庆,如cmd1-2 || cmd3 && cmd4
就相當(dāng)于cmd1-2_3 && cmd4
筐带,cmd4
是否會(huì)執(zhí)行,決定于cmd1-2_3
是否失敗缤灵,以具體例子說明:
# echo hello && echo ok || echo world && rm dsdfsf || echo end
hello
ok
rm: cannot remove ‘dsdfsf’: No such file or directory
end
這行命令相當(dāng)于cmd1 && cmd2 || cmd3 && cmd4 || cmd5
伦籍,可以看出cmd1
,cmd2
腮出,cmd4
還是有cmd5
被執(zhí)行了帖鸦,而cmd3
沒有執(zhí)行。咱們來解析一下胚嘲,為何是如此的執(zhí)行結(jié)果作儿。首先,shell
從左往右掃描執(zhí)行:
- 發(fā)現(xiàn)
cmd1 && cmd2
馋劈,由&&
連成一個(gè)命令cmd1-2
攻锰,因?yàn)閮蓚€(gè)命令都是成功的晾嘶,所以都被執(zhí)行了,這樣可以認(rèn)為cmd1-2
成功 - 執(zhí)行成功之后口注,接下來是
||
操作符,這里并不會(huì)因?yàn)榍懊娴拿钍浅晒Φ木椋辉賵?zhí)行后面所有的命令寝志,而是||
操作符相當(dāng)于將cmd1-2
與cmd3
連接成了cmd1-2_3
,因?yàn)?code>cmd1-2成功了策添,所以cmd3
不再執(zhí)行材部,但是cmd1-2_3
相當(dāng)于執(zhí)行成功了 - 繼續(xù)執(zhí)行,發(fā)現(xiàn)是
&&
操作符唯竹,同樣將cmd1-2_3
與cmd4
連接起來乐导,記為cmd1-2_3-4
,因?yàn)?code>cmd1-2_3執(zhí)行成功了浸颓,所以cmd4
也被執(zhí)行物臂,但是cmd4
執(zhí)行失敗了,所以cmd1-2_3-4
相當(dāng)于執(zhí)行失敗 - 繼續(xù)執(zhí)行产上,發(fā)現(xiàn)是
||
操作符棵磷,同樣將cmd1-2_3-4
與cmd5
連成cmd1-2_3-4_5
,因?yàn)?code>cmd1-2_3-4執(zhí)行失敗晋涣,所以cmd5
被執(zhí)行
可見仪媒,shell
永遠(yuǎn)都是從左往右掃描執(zhí)行,&&
跟||
會(huì)將前后兩個(gè)命令連接起來谢鹊,根據(jù)兩種操作符的規(guī)則就可以知道多個(gè)連起來的命令是如何執(zhí)行的了算吩。
#
符號
跟其它很多語言一樣,#
在shell
里面用來注釋佃扼。
\
轉(zhuǎn)義符號
\
符號可以用來轉(zhuǎn)義一些特殊符號偎巢,如$
,#
等兼耀。
特別指出的是艘狭,如果轉(zhuǎn)義符號放在行末單獨(dú)使用,則用來連接下一行翠订。
shell變量
基本概念
定義跟引用
shell
中也可以使用變量巢音,變量不需要像其它語言一樣需要預(yù)先申明。shell
中賦值給一個(gè)不存在的變量就相當(dāng)于定義了變量尽超,如name="Mr. Hao"
官撼,就定義了name
變量,后續(xù)如果再對name
賦值似谁,就相當(dāng)于改變改變量的值傲绣。與很多語言不同的是掠哥,shell
中變量引用以$
符號開頭,后面跟變量的名字秃诵。如前面的變量续搀,引用如下echo "$name"
。需要注意的是菠净,在shell
中禁舷,變量名是大小寫敏感的。
在shell
展開中會(huì)自動(dòng)展開變量的引用毅往,即便該變量處在雙引號中牵咙。但是,如果變量引用在單引號中攀唯,shell
不會(huì)對其進(jìn)行解析洁桌。
# name="Mr. Hao"
# echo "$name"
Mr. Hao
# set -x
# echo '$name'
+ echo 'Mr. Hao'
$name
查找變量
可以使用set
命令來查找所定義的變量:
# set | grep -E '^name='
name='Mr. Hao'
刪除變量
與很多語言不同的是,在shell
中定義的變量是可以刪除的侯嘀,使用unset
命令刪除定義的變量另凌。
# set | grep -E '^name='
name='Mr. Hao'
# unset name
# set | grep -E '^name='
export
聲明
通常情況下,shell
在執(zhí)行命令的時(shí)候會(huì)為該命令創(chuàng)建子進(jìn)程戒幔。如果希望將當(dāng)前的變量作用到子進(jìn)程途茫,則需要將變量export
聲明,這種變量稱之為環(huán)境變量溪食,如:
# var1="hello"
# export var2="world"
# bash
# echo "var1=$var1, var2=$var2"
var1=, var2=world
其中囊卜,bash
命令開啟了一個(gè)新的shell
,可見只有export
聲明的變量在新的shell
中才是可見的错沃。環(huán)境變量可以通過env
命令列舉出來栅组,在后面一節(jié)會(huì)詳細(xì)講述。此外枢析,如果需要將非export
變量重新聲明為export
變量玉掸,則只需要用export
重新聲明一下即可:
# var1=hello
# env | grep var1
# export var1
# env | grep var1
var1=hello
env
命令
如果需要查看當(dāng)前shell
中有哪些export
聲明的變量,可以使用env
命令醒叁,該命令會(huì)列出當(dāng)前所有export
聲明的變量司浪。請注意與set
命令的區(qū)別,set
命令會(huì)列出所有的變量把沼,包括哪些不是export
聲明的變量啊易。通常,我們把env
命令輸出的變量稱之為環(huán)境變量
饮睬。
此外租谈,env
也常用來為子shell
預(yù)先定義一些臨時(shí)變量,如:
# var1="hello"
# env var1="tmp" bash -c 'echo "$var1"'
tmp
# echo $var1
hello
其中捆愁,用env
命令定義了臨時(shí)變量var1
割去,然后bash
命令開啟了一個(gè)子shell
窟却,并在子shell
中執(zhí)行了echo "$var1"
命令∩肽妫可見夸赫,輸出了定義的臨時(shí)變量,在命令結(jié)束后咖城,又回到之前的shell
茬腿,輸出的也是之前shell
中定義的值。當(dāng)然酒繁,在使用env
定義臨時(shí)變量的時(shí)候滓彰,為了方便控妻,通常我們可以省略env
命令州袒,如:
# var1="hello"
# var1="tmp" bash -c 'echo "$var1"'
tmp
# echo $var1
hello
另外,env
命令還有一種常用的用法弓候,就是用來開啟一個(gè)干凈的子shell
郎哭,即在子shell
中不繼承所有的變量,即便這些變量在之前的shell
中采用export
聲明菇存,此時(shí)env
命令需要加入-i
的參數(shù)夸研,如:
# export var1="hello world"
# bash -c 'echo "var1=$var1"'
var1=hello world
# env -i bash -c 'echo "var1=$var1"'
var1=
可見,使用env -i
之后依鸥,即便var1
被export
聲明亥至,但是在子shell
中也沒有被繼承。
變量解釋
在前面章節(jié)贱迟,我們知道shell
采用$
符號引用變量姐扮,在$
符號后緊跟變量的名字。而shell
在提取變量名字的時(shí)候一般以非字母數(shù)字(non-alphanumeric)為邊界衣吠,這有時(shí)候就會(huì)產(chǎn)生問題茶敏,如:
# prefix=Super
# echo Hello $prefixman and $prefixgirl
Hello and
可見,shell
并不能提取我們定義的變量prefix
缚俏,因?yàn)槠浜蟛]有非字母數(shù)字的字符為界惊搏。這種情況下,我們可以使用{}
將變量名保護(hù)起來忧换。
# prefix=Super
# echo Hello ${prefix}man and ${prefix}girl
Hello Superman and Supergirl
非綁定(unbound)變量
所謂非綁定(unbound)變量其實(shí)指的是沒有預(yù)先定義的變量恬惯,或者說不存在的變量。默認(rèn)情況下亚茬,shell
在解釋這種變量的時(shí)候會(huì)以空字符串替代:
# echo $unbound_var
如果需要shell
在這種情況下報(bào)錯(cuò)宿崭,可以配置shell
選項(xiàng)set -o nounset
,或者簡寫為set -u
:
# echo $unbound_var
bash: unbound_var: unbound variable
# set +u
# echo $unbound_var
當(dāng)然才写,由例子中可以看到葡兑,要取消該配置奖蔓,可以相應(yīng)的設(shè)置set +o nounset
,或者簡寫為set +u
讹堤。
特殊變量
在shell
中預(yù)定義了很多特殊的變量吆鹤,這一節(jié)咱們來說一下常見的幾個(gè)變量。
$PS1
變量
在shell
終端輸入命令時(shí)洲守,咱們總是可以看到在輸入行首總是會(huì)有提示符疑务,如:
mrhao:~$
其中,mrhao:~$
就是提示符梗醇,這個(gè)字串實(shí)際上是由shell
變量$PS1
決定的知允。如果咱們改變一下該變量的值,提示符也會(huì)相應(yīng)的改變:
mrhao:~$ PS1="hello > "
hello > echo "PS1 value is '$PS1'"
PS1 value is 'hello > '
hello >
為了方便在提示符中顯示系統(tǒng)的某些實(shí)時(shí)信息叙谨,$PS1
變量定義了一些特殊的字符:
字符 | 說明 |
---|---|
\w | 表示工作目錄 |
\u | 表示用戶名 |
\h | 表示系統(tǒng)的hostname |
當(dāng)然温鸽,這里只列舉了幾個(gè),詳細(xì)的可以查看Linux手冊手负。另外涤垫,$PS1
中還可以對對其中不同部分采用不同顏色顯示,如:
# RED='\[\033[01;31m\]'
# WHITE='\[\033[01;00m\]'
# GREEN='\[\033[01;32m\]'
# BLUE='\[\033[01;34m\]'
# PS1="$GREEN\u$WHITE@$BLUE\h$WHITE\w\$ "
mrhao@mrhao-host~$ echo "$PS1"
\[\033[01;32m\]\u\[\033[01;00m\]@\[\033[01;34m\]\h\[\033[01;00m\]\w$
$PATH
變量
當(dāng)我們在Linux的terminal里面輸入命令的時(shí)候竟终,shell
需要在一系列的目錄中查找輸入的命令蝠猬,如果沒有查找到會(huì)直接報(bào)command not found
的錯(cuò)誤。而這些查找的目錄就定義在$PATH
變量中统捶。
# echo $PATH
/opt/rh/rh-python34/root/usr/bin:/usr/java/default/bin/:/usr/local/git/bin:/opt/ActiveTcl-8.5/bin:/root/perl5/bin:/root/env/maven/apache-maven-3.3.3/bin:/root/soft/wrk/wrk-4.0.1:/root/usr/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
其中榆芦,每個(gè)目錄以:
隔開,如果需要增加目錄喘鸟,可以:
# PATH=$PATH:/opt/local/bin
# echo $PATH
/opt/rh/rh-python34/root/usr/bin:/usr/java/default/bin/:/usr/local/git/bin:/opt/ActiveTcl-8.5/bin:/root/perl5/bin:/root/env/maven/apache-maven-3.3.3/bin:/root/soft/wrk/wrk-4.0.1:/root/usr/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/opt/local/bin
加入新的路徑的時(shí)候請務(wù)必帶上之前的路徑匆绣,
$PATH:<new path>
否則,很多默認(rèn)的系統(tǒng)路徑將被覆蓋迷守,導(dǎo)致很多命令失效犬绒。
特別注意的是,$PATH
變量中目錄的順序是很重要的兑凿,如果shell
在前面的目錄中找到了命令凯力,則不會(huì)查找后面的目錄。如果你想把某個(gè)重名的命令優(yōu)先執(zhí)行礼华,就需要把它對應(yīng)的目錄放在$PATH
的前面咐鹤。
網(wǎng)絡(luò)代理變量
在Linux系統(tǒng)中,很多時(shí)候我們需要訪問外部網(wǎng)絡(luò)圣絮,比如使用curl
命令下載文件等等祈惶。而有的時(shí)候,訪問訪問外部網(wǎng)絡(luò)咱們需要設(shè)置代理,在Linux系統(tǒng)中捧请,使用網(wǎng)絡(luò)代理非常簡單凡涩,只要配置幾個(gè)變量即可:
變量 | 說明 |
---|---|
http_proxy | 設(shè)置訪問http 請求所需要的代理,如http_proxy=http://10.10.10.100:80
|
https_proxy | 設(shè)置訪問https 請求所需要的代理疹蛉,如https_proxy=http://10.10.10.100:80
|
ftp_proxy | 設(shè)置訪問ftp 請求所需要的代理活箕,如ftp_proxy=http://10.10.10.100:80
|
no_proxy | 設(shè)置哪些域名或者IP不需要走代理,如no_proxy=localhost,127.0.0.1
|
$PWD
變量
PWD
變量是一個(gè)由shell
自動(dòng)設(shè)置的變量可款,其值表示當(dāng)前目錄的絕對路徑育韩,與命令pwd
輸出相同。
shell
嵌入與shell
選項(xiàng)
shell
嵌入(shell embedding)
shell
可以嵌入在同一個(gè)命令行中闺鲸,也就是shell
在掃描解釋命令行的時(shí)候筋讨,可能會(huì)從當(dāng)前的shell
進(jìn)程中fork
出一個(gè)新的shell
進(jìn)程,并將有關(guān)命令放在新進(jìn)程中運(yùn)行摸恍。如下例:
# var1=hello
# echo $(var1=world; echo $var1)
world
# echo $var1
hello
如其中$()
便開啟了一個(gè)新的shell
進(jìn)程悉罕,或者成為子shell
,并在此shell
中運(yùn)行命令var1=world; echo $var1
误墓,此時(shí)輸出的是子shell
中定義的var1
蛮粮。當(dāng)命令結(jié)束后益缎,子shell
進(jìn)程退出谜慌,并將輸出的結(jié)果world
返回給之前的shell
(或者父shell
)的echo
命令,父shell
最后輸出world
莺奔。而且欣范,在子shell
中定義相同的var1
變量并不會(huì)改變父shell
中的變量。
特別注意的是令哟,因?yàn)樽?code>shell是fork
出來的進(jìn)程恼琼,根據(jù)Linux進(jìn)程fork
的特點(diǎn),子進(jìn)程將共享父進(jìn)程的數(shù)據(jù)空間屏富,而只在寫的時(shí)候拷貝新的數(shù)據(jù)空間晴竞,因此,創(chuàng)建出來的子shell
是會(huì)繼承所有父shell
的變量狠半,不論該變量是否被export
聲明
# var1=hello
# var2="$(echo $var1 world)"
# echo $var2
hello world
可見噩死,雖然var1
變量沒有export
聲明,但是在子shell
中還是可見的神年。這點(diǎn)與使用bash -c
開啟的shell
是不同的已维。
用$()
可以將子shell
嵌入到命令行中,當(dāng)然已日,$()
是可以嵌套使用的垛耳,這樣可以用來在子shell
中開啟它的子shell
。
# A=shell
# echo $C$B$A $(B=sub;echo $C$B$A; echo $(C=sub;echo $C$B$A))
shell subshell subsubshell
反引號(backticks)
在上面我們可以通過$()
將子shell
嵌入命令行中,為了方便堂鲜,我們同樣可以用反引號`
將子shell
嵌入栈雳。
# var1=hello
# echo `var1=world; echo $var1`
world
# echo $var1
hello
但是,使用反引號不能夠嵌套子shell
缔莲,因此如果需要嵌套子shell
時(shí)甫恩,只能使用$()
。
反引號跟單引號是本質(zhì)的不同的酌予,單引號與雙引號一樣磺箕,用來將連續(xù)的字串作為整體引起來,只不過單引號中將不執(zhí)行變量的引用解析抛虫,而反引號則是嵌入子
shell
松靡。
shell
選項(xiàng)
其實(shí)在前面咱們已經(jīng)使用了不少shell
的選項(xiàng),如set -u
在變量不存在是報(bào)錯(cuò)建椰,set -x
將shell
展開的結(jié)果顯示出來等雕欺。此外,可以才用echo $-
將當(dāng)期設(shè)置的shell
選項(xiàng)打印出來棉姐。
shell
歷史記錄
在shell
中執(zhí)行命令的時(shí)候屠列,shell
會(huì)將最近的命令使用歷史記錄下來,這樣你可以很方便的查看最近做了什么操作伞矩。
查看歷史記錄
命令history
可以用來查看shell
的歷史記錄笛洛,里面記錄了你最近輸入的所有命令。當(dāng)然乃坤,很多時(shí)候你更加關(guān)心最近的幾個(gè)命令苛让,你可以使用history 10
來顯示最近的10個(gè)命令。另外湿诊,shell
通常還會(huì)將最近的歷史記錄寫在~/.bash_history
文件中狱杰,因此查看該文件同樣可以查看歷史記錄。
執(zhí)行歷史的命令
shell
提供了很多高級用法使得你可以很方便的執(zhí)行以前執(zhí)行過的命令厅须。
首先仿畸,咱們先顯示一下過去的10個(gè)命令,可以看到每個(gè)命令前面都有其對應(yīng)的序號朗和。
# history 10
1000 history
1001 history 10
1002 echo "hello world"
1003 ls -l
1004 ps -ef | grep named
1005 env | grep http
1006 grep hello /var/log/messages
1007 tmux ls
1008 find . -name "hello"
1009 history 10
下面列舉比較常用的shell
重復(fù)執(zhí)行歷史記錄中命令的方法:
命令 | 說明 |
---|---|
!! | 在shell 中輸入兩個(gè)感嘆號會(huì)執(zhí)行上一個(gè)命令 |
!keyword | 輸入一個(gè)感嘆號后跟關(guān)鍵字错沽,會(huì)搜索歷史記錄中最先以該關(guān)鍵字開始的命令。如!find 會(huì)執(zhí)行序號為1008的命令例隆。 |
!n | 其中n代表歷史記錄中的序號甥捺,表示執(zhí)行序號為n的命令。 |
另外镀层,對于!keyword
的用法镰禾,還有一個(gè)高級功能皿曲,你可以將符合該條件的命令進(jìn)行改造后執(zhí)行,如:
# echo "test1"
test1
# !ec:s/1/2/
echo "test2"
test2
其中吴侦,:s/1/2/
將命令echo "test1"
替換成echo "test2"
然后執(zhí)行了屋休。
搜索歷史記錄
在shell
終端中按Ctrl-r會(huì)打開shell
的搜索模式,在改模式下輸入關(guān)鍵字會(huì)顯示最近包含改關(guān)鍵字的命令备韧,再按一下Ctrl-r會(huì)繼續(xù)顯示前面一條符合條件的命令劫樟,找到你需要的命令后回車就可以執(zhí)行改命令了。
修改歷史記錄的有關(guān)配置
有多個(gè)配置可以用來改變歷史記錄的有關(guān)信息织堂,通常都是通過有關(guān)環(huán)境變量來配置:
環(huán)境變量 | 說明 |
---|---|
$HISTSIZE | 這個(gè)變量用來配置shell 應(yīng)該保持多少行的歷史記錄叠艳,在很多發(fā)行版本中,默認(rèn)值一般為500或者1000 |
$HISTFILE | 這個(gè)變量用來配置歷史記錄文件存放的位置易阳,通常來講附较,默認(rèn)路徑為~/.bash_history
|
$HISTFILESIZE | 這個(gè)變量用來配置歷史記錄文件可以存放多少行的歷史記錄 |
阻止記錄某些命令
在有些時(shí)候,我們并不想把某些命令記錄在歷史記錄中潦俺,比如有的命令里面包括了敏感信息如密碼等拒课。在新版本的shell
中,通常我們可以在輸入的命令前面加入空格事示,這樣shell
就不會(huì)記錄這樣的命令早像,當(dāng)然,如果你的發(fā)行版本默認(rèn)并不支持肖爵,你可以配置環(huán)境變量來打開這個(gè)功能:
export HISTIGNORE="[ \t]*"
例如:
# history 5
1023 ls -l
1024 echo ""
1025 history 5
1026 ls
1027 history 5
# echo "password=123456"
password=123456
# history 5
1025 history 5
1026 ls
1027 history 5
1028 echo "password=123456"
1029 history 5
# export HISTIGNORE="[ \t]*"
# history 5
1027 history 5
1028 echo "password=123456"
1029 history 5
1030 export HISTIGNORE="[ \t]*"
1031 history 5
# echo "password=123456"
password=123456
# history 5
1027 history 5
1028 echo "password=123456"
1029 history 5
1030 export HISTIGNORE="[ \t]*"
1031 history 5
可見卢鹦,在設(shè)置$HISTIGNORE
變量之后,在前面加了空格的命令將不再記錄遏匆。這在保護(hù)敏感信息的時(shí)候非常有用法挨。
文件匹配(File Globbing)
文件匹配(File Globbing)又成為動(dòng)態(tài)文件名生成谁榜,用它可以非常方便的在shell
中輸入文件名幅聘。
*
星號
*
星號在shell
中用來匹配任意數(shù)量的字符,比如文件名File*.mp4
窃植,將匹配以File
開頭帝蒿,.mp4
結(jié)尾的任何文件名。shell
在掃描解釋命令的時(shí)候會(huì)自動(dòng)去查找符合該匹配的所有文件或目錄巷怜。當(dāng)然葛超,你也可以只用*
來匹配所有的文件及目錄,但請注意延塑,只使用*
跟不帶*
還是有所區(qū)別的绣张,
# ls
definition.yaml example __init__.py tags.yaml test.py test_sample.html test_sample.py
# ls *
definition.yaml __init__.py tags.yaml test.py test_sample.html test_sample.py
example:
testcase
可見,帶上*
后不僅把當(dāng)前目錄的所有文件及目錄顯示出來关带,而且還把目錄下的內(nèi)容顯示出來了侥涵。
?
問號
問號用來匹配一個(gè)字符,如File?.mp4
可以匹配File1.mp4
。
[]
方括號
[]
方括號也用來匹配一個(gè)字符芜飘,但是在括號里面可以指定一個(gè)字符集用來限定匹配的字符必須在該字符集內(nèi)务豺,字符集里面的字符順序沒有關(guān)系。
# ls
file1 file2 file3 File4 File55 FileA fileab Fileab FileAB fileabc
# ls File[5A]
FileA
# ls File[A5]
FileA
# ls File[A5][5b]
File55
如果需要匹配不在某個(gè)字符集里面的字符嗦明,可以在[]
第一個(gè)字符加入!
:
# ls file[!5]*
file1 file2 file3 fileab fileabc
特別的笼沥,為了方便,[]
中可以使用-
來定義一些連續(xù)的字符集(Range匹配)娶牌,常用的這類字符集包括:
字符集 | 說明 |
---|---|
0-9 | 表示數(shù)字字符集 |
a-z | 表示小寫字母字符集 |
A-Z | 表示大寫字母字符集 |
當(dāng)然奔浅,你也不必要把所有范圍都包括在內(nèi),如[a-d]
可以用來限定從a
到d
的小寫字母集诗良。另外乘凸,用-
連起來的字符集還可以跟其它字符集一起使用,如[a-d_]
表示a
到d
的小寫字母加上_
所組成的字符集累榜。
-
Range匹配的大小寫問題
對于
[]
的Range匹配营勤,還有一點(diǎn)很重要。在很多發(fā)行版本中壹罚,默認(rèn)情況下葛作,[]
的Range匹配是忽略大小寫的# ls Test1 test2 # ls [a-z]* Test1 test2 # ls [A-Z]* Test1 test2 # ls [t]* test2 # ls [T]* Test1
注意,是
[]
的Range匹配會(huì)忽略大小寫猖凛,而如果不是Range匹配還是大小寫敏感的:# ls Test1 test2 # ls [T]* Test1 # ls [t]* test2
如果需要大小寫敏感赂蠢,可以設(shè)置環(huán)境變量
LC_ALL
:# LC_ALL=C # ls [a-z]* test2 # ls [A-Z]* Test1
當(dāng)然,請務(wù)必注意辨泳,
LC_ALL
的會(huì)改變當(dāng)前的語言環(huán)境虱岂,還請慎重使用,建議只在臨時(shí)的子shell
中使用菠红。
阻止文件匹配(File Globbing)
有時(shí)候我們就是需要輸出*
等匹配符號第岖,這個(gè)時(shí)候就需要阻止shell
做相應(yīng)的匹配∈运荩可以使用轉(zhuǎn)義符號\
來做到這點(diǎn)蔑滓,或者將匹配符號放在引號中:
# echo *
Test1 test2
# echo \*
*
# echo '*'
*
# echo "*"
*
本博文還可以在博主個(gè)人主頁中找到。