簡(jiǎn)介
查看當(dāng)前設(shè)備默認(rèn)shell:
$ echo $SHELL
/bin/bash
但是當(dāng)前使用的shell并不一定是上述shell简肴。一般來(lái)說(shuō),ps
命令結(jié)果的倒數(shù)第二行是當(dāng)前 Shell:
$ ps
PID TTY TIME CMD
4467 pts/0 00:00:00 bash
5379 pts/0 00:00:00 ps
查看當(dāng)前的 Linux 系統(tǒng)安裝的所有 Shell:
$ cat /etc/shells
上面例子中百侧,完整的提示符是[user@hostname] $
(用戶名@主機(jī)名 $)
注意砰识,根用戶(root)的提示符,不以美元符號(hào)$
結(jié)尾佣渴,而以井號(hào)#
結(jié)尾
可以輸入bash命令啟動(dòng) Bash:
$ bash
退出 Bash 環(huán)境辫狼,可以使用exit命令,也可以同時(shí)按下Ctrl + d:
$ exit
用戶可以通過(guò)bash命令的--version參數(shù)或者環(huán)境變量$BASH_VERSION辛润,查看本機(jī)的 Bash 版本:
$ bash --version
GNU bash膨处,版本 5.0.3(1)-release (x86_64-pc-linux-gnu)
# 或者
$ echo $BASH_VERSION
5.0.3(1)-release
echo命令
$ echo hello world
hello world
$ echo "hello world"
hello world
多行輸出要加""
$ echo "<HTML>
<HEAD>
<TITLE>Page Title</TITLE>
</HEAD>
<BODY>
Page body.
</BODY>
</HTML>"
-n
參數(shù)用于取消回車(chē)符(注意第二行$符位置):
$ echo -n hello world
hello world$
;
用于分割語(yǔ)句:
$ echo -n a;echo b
ab
-e
參數(shù)會(huì)解釋引號(hào)(雙引號(hào)和單引號(hào))里面的特殊字符:
$ echo "Hello\nWorld"
Hello\nWorld
$ echo -e "Hello\nWorld"
Hello
World
命令分行:
$ echo foo \
bar
#等價(jià)于
echo foo bar
foo bar
控制命令執(zhí)行順序:
#使用分號(hào)時(shí),第二個(gè)命令總是接著第一個(gè)命令執(zhí)行,不管第一個(gè)命令執(zhí)行成功或失敗真椿。
Command1 ; Command2
#使用&&時(shí)鹃答,如果Command1命令運(yùn)行成功,則繼續(xù)運(yùn)行Command2命令瀑粥。
Command1 && Command2
#使用||時(shí)挣跋,如果Command1命令運(yùn)行失敗,則繼續(xù)運(yùn)行Command2命令狞换。
Command1 || Command2
type命令用來(lái)判斷命令的來(lái)源:
$ type echo
echo is a shell builtin
$ type ls
ls is hashed (/bin/ls)
可以看到避咆,echo是內(nèi)部命令,ls是外部程序(/bin/ls)
-a
參數(shù)用于要查看一個(gè)命令的所有定義:
$ type -a echo
echo is shell builtin
echo is /usr/bin/echo
echo is /bin/echo
可以看到修噪,echo命令既是內(nèi)置命令查库,也有對(duì)應(yīng)的外部程序。
-t
參數(shù)可以返回一個(gè)命令的類(lèi)型:別名(alias)黄琼,關(guān)鍵詞(keyword)樊销,函數(shù)(function),內(nèi)置命令(builtin)和文件(file):
$ type -t bash
file
$ type -t if
keyword
可以看到脏款,bash是文件围苫,if是關(guān)鍵詞。
模式擴(kuò)展
bash關(guān)閉模式擴(kuò)展:
$ set -o noglob
# 或者
$ set -f
bash打開(kāi)模式擴(kuò)展:
$ set +o noglob
# 或者
$ set +f
波浪線~
會(huì)自動(dòng)擴(kuò)展成當(dāng)前用戶的主目錄:
$ echo ~
/home/me
# 進(jìn)入 /home/me/foo 目錄
$ cd ~/foo
~user
表示擴(kuò)展成用戶user的主目錄撤师。
$ echo ~foo
/home/foo
$ echo ~root
/root
如果~user的
user是不存在的用戶名剂府,則波浪號(hào)擴(kuò)展不起作用:
$ echo ~nonExistedUser
~nonExistedUser
~+
會(huì)擴(kuò)展成當(dāng)前所在的目錄,等同于pwd命令:
$ cd ~/foo
$ echo ~+
/home/me/foo
?
字符代表文件路徑里面的任意單個(gè)字符剃盾,不包括空字符:
# 存在文件 a.txt 和 b.txt
$ ls ?.txt
a.txt b.txt
# 存在文件 a.txt腺占、b.txt 和 ab.txt
$ ls ??.txt
ab.txt
# 當(dāng)前目錄為空目錄
$ echo ?.txt
?.txt
*
字符代表文件路徑里面的任意數(shù)量的任意字符,包括零個(gè)字符:
# 存在文件 a.txt痒谴、b.txt 和 ab.txt
$ ls *.txt
a.txt b.txt ab.txt
注意衰伯,*
不會(huì)匹配隱藏文件(以.開(kāi)頭的文件),即ls *
不會(huì)輸出隱藏文件积蔚。
如果要匹配隱藏文件意鲸,同時(shí)要排除.
和..
這兩個(gè)特殊的隱藏文件,可以與方括號(hào)擴(kuò)展結(jié)合使用:
$ echo .[!.]*
匹配子目錄文件:
# 子目錄有一個(gè) a.txt
# 無(wú)效的寫(xiě)法
$ ls *.txt
# 有效的寫(xiě)法
$ ls */*.txt
Bash 4.0 引入了一個(gè)參數(shù)globstar库倘,當(dāng)該參數(shù)打開(kāi)時(shí)临扮,允許**匹配零個(gè)或多個(gè)子目錄。因此教翩,**/*.txt
可以匹配頂層的文本文件和任意深度子目錄的文本文件杆勇。
方括號(hào)[]
擴(kuò)展匹配括號(hào)中任意字符:
# 存在文件 a.txt 和 b.txt
$ ls [ab].txt
a.txt b.txt
# 不存在文件 a.txt 和 b.txt
$ ls [ab].txt
ls: 無(wú)法訪問(wèn)'[ab].txt': 沒(méi)有那個(gè)文件或目錄
[^...]
和[!...]
表示匹配不在方括號(hào)里面的字符,這兩種寫(xiě)法是等價(jià)的:
# 存在 aaa饱亿、bbb蚜退、aba 三個(gè)文件
$ ls ?[!a]?
aba bbb
注意闰靴,如果需要匹配[
字符,可以放在方括號(hào)內(nèi)钻注,比如[[aeiou]
蚂且。如果需要匹配連字號(hào)-
,只能放在方括號(hào)內(nèi)部的開(kāi)頭或結(jié)尾幅恋,比如[-aeiou]
或[aeiou-]
杏死。
方括號(hào)擴(kuò)展有一個(gè)簡(jiǎn)寫(xiě)形式[start-end]
,表示匹配一個(gè)連續(xù)的范圍捆交。比如淑翼,[a-c]
等同于[abc]
,[0-9]
匹配[0123456789]
品追。
# 存在文件 a.txt玄括、b.txt 和 c.txt
$ ls [a-c].txt
a.txt
b.txt
c.txt
[!start-end]
表示匹配不屬于這個(gè)范圍的字符。比如肉瓦,[!a-zA-Z]表示匹配非英文字母的字符遭京。
大括號(hào)擴(kuò)展{...}
表示分別擴(kuò)展成大括號(hào)里面的所有值,各個(gè)值之間使用逗號(hào)分隔:
$ echo {1,2,3}
1 2 3
$ echo d{a,e,i,u,o}g
dag deg dig dug dog
$ echo Front-{A,B,C}-Back
Front-A-Back Front-B-Back Front-C-Back
注意泞莉,大括號(hào)擴(kuò)展不是文件名擴(kuò)展哪雕。它會(huì)擴(kuò)展成所有給定的值,而不管是否有對(duì)應(yīng)的文件存在:
$ ls {a,b,c}.txt
ls: 無(wú)法訪問(wèn)'a.txt': 沒(méi)有那個(gè)文件或目錄
ls: 無(wú)法訪問(wèn)'b.txt': 沒(méi)有那個(gè)文件或目錄
ls: 無(wú)法訪問(wèn)'c.txt': 沒(méi)有那個(gè)文件或目錄
另一個(gè)需要注意的地方是鲫趁,大括號(hào)內(nèi)部的逗號(hào)前后不能有空格热监。否則,大括號(hào)擴(kuò)展會(huì)失效:
#Bash認(rèn)為這不是大括號(hào)擴(kuò)展饮寞,而是三個(gè)獨(dú)立的參數(shù)。
$ echo {1 , 2}
{1 , 2}
逗號(hào)前面可以沒(méi)有值列吼,表示擴(kuò)展的第一項(xiàng)為空:
$ cp a.log{,.bak}
# 等同于
# cp a.log a.log.bak
大括號(hào)嵌套:
$ echo {j{p,pe}g,png}
jpg jpeg png
$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b
大括號(hào)也可以與其他模式聯(lián)用幽崩,并且總是先于其他模式進(jìn)行擴(kuò)展:
$ echo /bin/{cat,b*}
/bin/cat /bin/b2sum /bin/base32 /bin/base64 ... ...
# 基本等同于
$ echo /bin/cat;echo /bin/b*
由于大括號(hào)擴(kuò)展{...}
不是文件名擴(kuò)展,所以它總是會(huì)擴(kuò)展的寞钥。這與方括號(hào)擴(kuò)展[...]
完全不同慌申,如果匹配的文件不存在,方括號(hào)就不會(huì)擴(kuò)展理郑。這一點(diǎn)要注意區(qū)分:
# 不存在 a.txt 和 b.txt蹄溉,[]不會(huì)進(jìn)行擴(kuò)展
$ echo [ab].txt
[ab].txt
#但是{}會(huì)進(jìn)行擴(kuò)展
$ echo {a,b}.txt
a.txt b.txt
大括號(hào)擴(kuò)展有一個(gè)簡(jiǎn)寫(xiě)形式{start..end},表示擴(kuò)展成一個(gè)連續(xù)序列您炉。比如:
$ echo {a..c}
a b c
$ echo d{a..d}g
dag dbg dcg ddg
$ echo {5..1} #支持逆序
5 4 3 2 1
$ echo {a1..3c} #解釋器無(wú)法理解則原樣輸出
{a1..3c}
大括號(hào)擴(kuò)展的常見(jiàn)用途為新建一系列目錄:
#新建36個(gè)子目錄柒爵,每個(gè)子目錄的名字都是”年份-月份“
$ mkdir {2007..2009}-{01..12}
這個(gè)寫(xiě)法的另一個(gè)常見(jiàn)用途,是直接用于for循環(huán):
for i in {1..5}
do
echo $i
done
如果整數(shù)前面有前導(dǎo)0赚爵,擴(kuò)展輸出的每一項(xiàng)都有前導(dǎo)0:
$ echo {01..5}
01 02 03 04 05
$ echo {001..5}
001 002 003 004 005
這種簡(jiǎn)寫(xiě)形式還可以使用第二個(gè)雙點(diǎn)號(hào)(start..end..step)棉胀,用來(lái)指定擴(kuò)展的步長(zhǎng):
$ echo {0..8..2}
0 2 4 6 8
多個(gè)簡(jiǎn)寫(xiě)形式連用法瑟,會(huì)有循環(huán)處理的效果:
$ echo {a..c}{1..3}
a1 a2 a3 b1 b2 b3 c1 c2 c3
bash變量:
$ echo $SHELL
/bin/bash
$ echo ${SHELL}
/bin/bash
${!string*}
或${!string@}
返回所有匹配給定字符串string的變量名:
$ echo ${!S*}
SECONDS SHELL SHELLOPTS SHLVL SSH_AGENT_PID SSH_AUTH_SOCK
上面例子中,${!S*}
擴(kuò)展成所有以S開(kāi)頭的變量名唁奢。
$(...)
可以擴(kuò)展成另一個(gè)命令的運(yùn)行結(jié)果霎挟,該命令的所有輸出都會(huì)作為返回值。同樣麻掸,子命令放在反引號(hào)中也可以:
$ echo $(date)
Tue Jan 28 00:01:13 CST 2020
$ echo `date`
Tue Jan 28 00:01:13 CST 2020
$(...)
可以嵌套酥夭,比如$(ls $(pwd))
。
$((...))
可以擴(kuò)展成整數(shù)運(yùn)算的結(jié)果
$ echo $((2 + 2))
4
[[:class:]]
表示一個(gè)字符類(lèi)脊奋,擴(kuò)展成某一類(lèi)特定字符之中的一個(gè):
#輸出所有大寫(xiě)字母開(kāi)頭的文件名
$ echo [[:upper:]]*
#輸出所有所有非數(shù)字開(kāi)頭的文件名
$ echo [![:digit:]]*
Bash 允許文件名使用通配符熬北,即文件名包括特殊字符。這時(shí)引用文件名狂魔,需要把文件名放在單引號(hào)或雙引號(hào)里面:
$ touch 'fo*'
$ ls
fo*
量詞語(yǔ)法用來(lái)控制模式匹配的次數(shù)蒜埋。它只有在 Bash 的extglob參數(shù)打開(kāi)的情況下才能使用,不過(guò)一般是默認(rèn)打開(kāi)的最楷。下面的命令可以查詢:
$ shopt extglob
extglob on
如果extglob
參數(shù)是關(guān)閉的整份,可以用下面的命令打開(kāi):
$ shopt -s extglob
兩次語(yǔ)法如下:
?(pattern-list):模式匹配零次或一次。
*(pattern-list):模式匹配零次或多次籽孙。
+(pattern-list):模式匹配一次或多次烈评。
@(pattern-list):只匹配一次模式。
!(pattern-list):匹配給定模式以外的任何內(nèi)容犯建。
舉例:
#?(.)匹配零個(gè)或一個(gè)點(diǎn)
$ ls abc?(.)txt
abctxt abc.txt
#?(def)匹配零個(gè)或一個(gè)def
$ ls abc?(def)
abc abcdef
#@(.txt|.php)匹配文件有且只有一個(gè).txt或.php后綴名
$ ls abc@(.txt|.php)
abc.php abc.txt
#+(.txt)匹配文件有一個(gè)或多個(gè).txt后綴名
$ ls abc+(.txt)
abc.txt abc.txt.txt
#!(b)表示匹配單個(gè)字母b以外的任意內(nèi)容讲冠,所以除了ab.txt以外,其他文件名都能匹配
$ ls a!(b).txt
a.txt abb.txt ac.txt
shopt
命令可以調(diào)整 Bash 的行為适瓦。它有好幾個(gè)參數(shù)跟通配符擴(kuò)展有關(guān):
# 打開(kāi)某個(gè)參數(shù)
$ shopt -s [optionname]
# 關(guān)閉某個(gè)參數(shù)
$ shopt -u [optionname]
# 查詢某個(gè)參數(shù)關(guān)閉還是打開(kāi)
$ shopt [optionname]
dotglob
參數(shù)可以讓擴(kuò)展結(jié)果包括隱藏文件(即點(diǎn)開(kāi)頭的文件):
$ shopt -s dotglob
$ ls *
abc.txt .config
nullglob
參數(shù)可以讓通配符不匹配任何文件名時(shí)竿开,返回空字符:
#默認(rèn)情況下:
$ rm b*
rm: 無(wú)法刪除'b*': 沒(méi)有那個(gè)文件或目錄
#nullglob打開(kāi)時(shí),由于沒(méi)有b*匹配的文件名玻熙,所以rm b*擴(kuò)展成了rm:
$ shopt -s nullglob
$ rm b*
rm: 缺少操作數(shù)
failglob
參數(shù)使得通配符不匹配任何文件名時(shí)否彩,Bash 會(huì)直接報(bào)錯(cuò),而不是讓各個(gè)命令去處理:
#由于b*不匹配任何文件名嗦随,Bash 直接報(bào)錯(cuò)了列荔,不再讓rm命令去處理
$ shopt -s failglob
$ rm b*
bash: 無(wú)匹配: b*
nocaseglob
參數(shù)可以讓通配符擴(kuò)展不區(qū)分大小寫(xiě):
$ shopt -s nocaseglob
$ ls /windows/program*
/windows/ProgramData
/windows/Program Files
/windows/Program Files (x86)
globstar
參數(shù)可以使得**匹配零個(gè)或多個(gè)子目錄:
$ shopt -s globstar
$ ls **/*.txt
a.txt sub1/b.txt sub1/sub2/c.txt
#如果globstar沒(méi)有打開(kāi),只能如下匹配:
$ ls *.txt */*.txt */*/*.txt
a.txt sub1/b.txt sub1/sub2/c.txt
轉(zhuǎn)義
\
用于轉(zhuǎn)義:
$ echo $date
$ echo \$date
$date
$ echo \\
\
$ echo \ #命令換行符
>
一些不可打印的字符(這些必須在引號(hào)內(nèi)):
\a:響鈴
\b:退格
\n:換行
\r:回車(chē)
\t:制表符
如果想要在命令行使用這些不可打印的字符枚尼,可以把它們放在引號(hào)里面贴浙,然后使用echo命令的-e參數(shù):
$ echo a\tb
atb
$ echo -e "a\tb"
a b
換行符(回車(chē))是一個(gè)特殊字符,表示命令的結(jié)束署恍,Bash 收到這個(gè)字符以后崎溃,就會(huì)對(duì)輸入的命令進(jìn)行解釋執(zhí)行。換行符前面加上反斜杠轉(zhuǎn)義锭汛,就使得換行符變成一個(gè)普通字符笨奠,Bash 會(huì)將其當(dāng)作長(zhǎng)度為0的空字符處理袭蝗,從而可以將一行命令寫(xiě)成多行:
$ mv \
/path/to/foo \
/path/to/bar
# 等同于
$ mv /path/to/foo /path/to/bar
Bash 允許字符串放在單引號(hào)或雙引號(hào)之中,加以引用般婆,變?yōu)樽址?/p>
$ echo '$USER'
$USER
$ echo '$((2+2))'
$((2+2))
由于反斜杠在單引號(hào)里面變成了普通字符到腥,所以如果單引號(hào)之中,還要使用單引號(hào)蔚袍,不能使用轉(zhuǎn)義乡范,需要在外層的單引號(hào)前面加上一個(gè)美元符號(hào)$
,然后再對(duì)里層的單引號(hào)轉(zhuǎn)義:
# 不正確
$ echo 'it\'s'
# 正確
$ echo $'it\'s'
不過(guò)啤咽,更合理的方法是改在雙引號(hào)之中使用單引號(hào):
$ echo "it's"
it's
注意晋辆,引號(hào)中的通配符不會(huì)發(fā)揮作用:
$ echo '*'
*
不過(guò)雙引號(hào)更寬松。美元符號(hào)$宇整、反引號(hào)`和反斜杠\瓶佳。這三個(gè)字符在雙引號(hào)之中,依然有特殊含義鳞青,會(huì)被 Bash 自動(dòng)擴(kuò)展:
$ echo "$SHELL" #$表示變量
/bin/bash
$ echo "`date`" #``表示執(zhí)行命令
Mon Jan 27 13:33:18 CST 2020
$ echo "I'd say: \"hello!\"" #\表示轉(zhuǎn)義
I'd say: "hello!"
$ echo "\\"
\
對(duì)比:
$ echo "\\"
\
$ echo '\\'
\\
換行符在雙引號(hào)之中霸饲,會(huì)失去特殊含義,Bash 不再將其解釋為命令的結(jié)束臂拓,只是作為普通的換行符厚脉。所以可以利用雙引號(hào),在命令行輸入多行文本:
$ echo "hello
world"
hello
world
雙引號(hào)還有一個(gè)作用胶惰,就是保存原始命令的輸出格式:
$ cal
一月 2020
日 一 二 三 四 五 六
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
# 單行輸出
$ echo $(cal)
一月 2020 日 一 二 三 四 五 六 1 2 3 ... 31
# 原始格式輸出
$ echo "$(cal)"
一月 2020
日 一 二 三 四 五 六
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
HERE文檔
Here 文檔(here document)是一種輸入多行字符串的方法傻工,格式如下:
<< token
text
token
它的格式分成開(kāi)始標(biāo)記(<< token)和結(jié)束標(biāo)記(token)。開(kāi)始標(biāo)記是兩個(gè)小于號(hào) + Here 文檔的名稱孵滞,名稱可以隨意取中捆,后面必須是一個(gè)換行符;結(jié)束標(biāo)記是單獨(dú)一行頂格寫(xiě)的 Here 文檔名稱坊饶,如果不是頂格轨香,結(jié)束標(biāo)記不起作用。兩者之間就是多行字符串的內(nèi)容幼东。
$ foo='hello world'
$ cat << _example_
$foo
"$foo"
'$foo'
"\\"
_example_
hello world
"hello world"
'hello world'
"\"
可以發(fā)現(xiàn),Here 文檔內(nèi)部會(huì)發(fā)生變量替換科雳,同時(shí)支持反斜杠轉(zhuǎn)義(在Here文檔中單引號(hào)和雙引號(hào)內(nèi)部均可以轉(zhuǎn)義根蟹,與非Here文檔環(huán)境不同),但是不支持通配符擴(kuò)展糟秘,雙引號(hào)和單引號(hào)也失去語(yǔ)法作用简逮,變成了普通字符。
如果不希望發(fā)生變量替換尿赚,可以把 Here 文檔的開(kāi)始標(biāo)記放在單引號(hào)之中:
$ foo='hello world'
$ cat << '_example_'
$foo
"$foo"
'$foo'
_example_
$foo
"$foo"
'$foo'
Here 文檔的本質(zhì)是重定向散庶,它將字符串重定向輸出給某個(gè)命令蕉堰,相當(dāng)于包含了echo命令:
$ command << token
string
token
# 等同于
$ echo string | command
所以,Here 字符串只適合那些可以接受標(biāo)準(zhǔn)輸入作為參數(shù)的命令悲龟,對(duì)于其他命令無(wú)效屋讶,比如echo命令就不能用 Here 文檔作為參數(shù)。
HERE字符串
它的作用是將字符串通過(guò)標(biāo)準(zhǔn)輸入须教,傳遞給命令:
有些命令直接接受給定的參數(shù)皿渗,與通過(guò)標(biāo)準(zhǔn)輸入接受參數(shù),結(jié)果是不一樣的轻腺,所以才有了這個(gè)語(yǔ)法乐疆,
$ cat <<< 'hi there'
# 等同于
$ echo 'hi there' | cat
md5sum命令只能接受標(biāo)準(zhǔn)輸入作為參數(shù),不能直接將字符串放在命令后面贬养,會(huì)被當(dāng)作文件名挤土,即md5sum ddd
里面的ddd會(huì)被解釋成文件名。這時(shí)就可以用 Here 字符串误算,將字符串傳給md5sum命令:
$ md5sum <<< 'ddd'
# 等同于
$ echo 'ddd' | md5sum