Linux系統(tǒng)介紹(三)shell基礎(chǔ)

概述

首先侠坎,咱們來了解一下蚁趁,什么是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ù)呀酸,分別為echoHello 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伦籍,可以看出cmd1cmd2腮出,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-2cmd3連接成了cmd1-2_3,因?yàn)?code>cmd1-2成功了策添,所以cmd3不再執(zhí)行材部,但是cmd1-2_3相當(dāng)于執(zhí)行成功了
  • 繼續(xù)執(zhí)行,發(fā)現(xiàn)是&&操作符唯竹,同樣將cmd1-2_3cmd4連接起來乐导,記為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-4cmd5連成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之后依鸥,即便var1export聲明亥至,但是在子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 -xshell展開的結(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]可以用來限定從ad的小寫字母集诗良。另外乘凸,用-連起來的字符集還可以跟其它字符集一起使用,如[a-d_]表示ad的小寫字母加上_所組成的字符集累榜。

  • 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è)人主頁中找到。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末遇绞,一起剝皮案震驚了整個(gè)濱河市键袱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌摹闽,老刑警劉巖蹄咖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異付鹿,居然都是意外死亡澜汤,警方通過查閱死者的電腦和手機(jī)铝量,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來银亲,“玉大人慢叨,你說我怎么就攤上這事∥耱穑” “怎么了拍谐?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長馏段。 經(jīng)常有香客問我轩拨,道長,這世上最難降的妖魔是什么院喜? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任亡蓉,我火速辦了婚禮,結(jié)果婚禮上喷舀,老公的妹妹穿的比我還像新娘砍濒。我一直安慰自己,他們只是感情好硫麻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布爸邢。 她就那樣靜靜地躺著,像睡著了一般拿愧。 火紅的嫁衣襯著肌膚如雪杠河。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天浇辜,我揣著相機(jī)與錄音券敌,去河邊找鬼。 笑死柳洋,一個(gè)胖子當(dāng)著我的面吹牛待诅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膳灶,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼咱士,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了轧钓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤锐膜,失蹤者是張志新(化名)和其女友劉穎毕箍,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體道盏,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡而柑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年文捶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒咳。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粹排,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涩澡,到底是詐尸還是另有隱情顽耳,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布妙同,位于F島的核電站射富,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粥帚。R本人自食惡果不足惜胰耗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芒涡。 院中可真熱鬧柴灯,春花似錦、人聲如沸费尽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽依啰。三九已至乎串,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間速警,已是汗流浹背叹誉。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闷旧,地道東北人长豁。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像忙灼,于是被迫代替她去往敵國和親匠襟。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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