許久之前,師弟問了我一個問題,為什么shell中添加環(huán)境變量的寫法是下面這種方式
PATH=~/.lib:$PATH; export PATH
而下面這種會報錯呢?
$PATH=~/.lib:$PATH; export PATH
當時我的回答是,"shell就是這樣子規(guī)定的呀"。 回答的同時,也突然間發(fā)現有些自己感覺很熟悉的概念,原來自己并沒有那么清楚,因此這一篇講講shell的命令行替換。先說結論
shell會在命令執(zhí)行前對命令行進行一些替換
shell替換有如下幾種:
| 替換 | 語法 | 含義 |
|---|---|---|
| 歷史 | ! | 之前使用命令 |
| 大括號 | {} | 指定的文本 |
| 代字號 | ~username | 用戶的主目錄 |
| 變量 | $, ${...} | shell和環(huán)境變量 |
| 算術 | $((..)) | 整數運算 |
| 命令替換 | `...`, $(...) | 運行在子shell里命令的輸出 |
| 路徑名 | *,?,[...],[^...] | 文件系統(tǒng)中匹配的文件名 |
歷史替換是以!開頭的替換方式,以下面歷史記錄為例

$ !! # 執(zhí)行上一個命令,即history
$ !1021 # 支持第1021個命令 即ls
$ !-2 # 執(zhí)行倒數第二個命令,即ls
大括號替換: 它會將{...}里的內容展開為多個單詞,可以快速創(chuàng)建有一定規(guī)律的文件. 下面這個命令就把"chap0{1..3}"替換成了chap01, chap02, chap03, 以及每個都還有一個html和text對應。
$ mkdir -p chap0{1..3}/{html,text}
$ tree chap0*
chap01
├── html
└── text
chap02
├── html
└── text
chap03
├── html
└── text
代字號代替: 我們經常會看到別人文章會寫用vim ~/.bashrc修改家目錄下的配置文件,其中~默認就會替代成自己家目錄路徑,可以用echo ~確認。
那么問題來了,如何我想快速到別人的家目錄下,應該怎么操作。只要在~加上別人的用戶名就行了。比如說我/home 下還有一個用戶叫做abc, 那么查看它家目錄下的內容就是
ls ~abc
注: ~a可以用tab補全成~abc哦
變量替換: shell會把${變量名}或者$變量名替換成變量所指代的具體字符,比如說我將abc指代為ls,那么shell就會將$abc解釋成ls,然后執(zhí)行ls
abc=ls
$abs
# Desktop bin biosoft blastdb miniconda3 ncbi
也就是$PATH=~/.lib:$PATH; export PATH報錯的原因是,shell在執(zhí)行命令前會把$PATH成原來PATH里的字符串,顯然無法達到修改PATH的目的
算術替換: shell命令行支持整數型的數學運算,下面的運算都是可以的,但是就別拿100/2.5這種浮點運算為難shell了。
echo $((1+2))
echo $((1-2))
echo $((100*101))
echo $((100/50))
命令替換:這個替換非常的實用,可以將shell命令的輸入結果立刻作為輸入,而不是額外創(chuàng)建一個變量命。有一個應用場景就是在的分析報告里加上完成時間點
touch reports.$(date +%d%b%Y).log
路徑名替換:路徑替換的語法就4種,*表示0或更多的任意字符,?表示一個任意字符,[...]表示括號內的字符之一,[^...]不包括括號內的字符
以上就是shell命令行替換的幾種形式。當然為了再一次強調"shell會在命令執(zhí)行前對命令行進行一些替換",下面舉一個反面例子來說明下。
Linux的/etc目錄下有很多以conf結尾的配置文件,我們可以用find命令快速的定位到它們。
find /etc -name *.conf
上面的命令看起來沒啥毛病,但是只要多做一件事情,就會有報錯哦
touch a.conf b.conf
find /etc -name *.conf
# 如下是報錯
find: paths must precede expression: b.conf
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec|time] [path...] [expression]
你會不會好奇,明明是相同的命令,卻又不同的境遇呢?讓我解釋下,在剛開始的時候,文件下面沒有"a.conf","b.conf",盡管shell看到"*"會有一種進行通配的沖動,但是很可惜沒有對象讓它統(tǒng)配。后來我們創(chuàng)建了這兩個文件,給shell找到通配的機會,于是實際執(zhí)行的命令就成了 "find /etc -name a.conf b.conf"。 由于后面這兩個是文件路徑,不符合find的命令要求,就導致了報錯。
其實報錯還好,有些時候沒有報錯,程序運行得到錯誤的結果反而更慘
如何避免這種錯誤呢?我們就需要用到"避免\*這個元字符被shell解釋。
除了雙引號,避免shell進行替換的符號還有 反斜杠\ , 和單引號 '. 單引號和雙引號的區(qū)別在于,單引號內部所有字符都是普通字符而已,而雙引號里的美元符號$, 感嘆號! 和反引號 ` 還能被shell解釋