shell程式設計中星號(asterisk "*")的坑
阿新 • • 發佈:2020-03-11
今天分享一個有關shell程式設計中由萬用字元引起的問題。
### 1. 問題程式碼
cat test.logs
```
4567890 *
##*************************************##
rtyuio**tyuio432
##*************************************##
*rtyuiop*2* yuiop
##*************************************##
rtyuiop(3 * 4)iuytr
##*************************************##
8765432
```
cat script.sh
```bash
#!/usr/bin/env bash
# 主要功能是將 非##開頭 的每行記錄寫入到檔案中,每個檔案儲存一行記錄
logsname=test.logs
i=100
while read line
do
if [[ $line =~ '##' ]];then
((i++))
else
echo $line >> $i.txt
fi
done < "${logsname}"
```
執行script.sh指令碼的結果:
![問題執行結果](https://img2020.cnblogs.com/blog/1938160/202003/1938160-20200310231814094-391135852.png)
從圖片上**紅框部分**可以看到:
> `4567890 *` 被替換為 `4567890 script.sh test.logs`
> `rtyuiop(3 * 4)iuytr` 被替換為 `rtyuiop(3 100.txt 101.txt 102.txt script.sh test.logs 4)iuytr`
其他行都正常列印結果,為什麼這兩行會有問題呢?其他行也有星號,為什麼沒有被替換呢?
### 2. 原因分析
根據輸出結果可以判斷出問題的程式碼:`echo $line >> $i.txt`
首先,介紹一下shell執行指令碼的原理:
1. shell讀取整個指令碼檔案,然後從上到下依次執行每一行
2. 假設當前line=4567890 *,當shell執行`echo $line > > $i.txt`時
1. 首先,shell負責替換`$line`的值為:4567890 *,此時程式碼是:`echo 4567890 * >> $i.txt`
2. 然後,shell在執行echo命令之前,檢查命令的引數中是否有**萬用字元**(PS:此時echo的引數:4567890 *)
3. 很明顯,`*`是萬用字元,shell負責解析萬用字元,shell會將萬用字元當作路徑或檔名在磁碟上搜尋可能的匹配:若符合要求的匹配存在,則進行替換(路徑擴充套件);否則就將該萬用字元作為一個普通字元引數傳遞給echo,然後再由echo進行處理。
4. 解析完萬用字元後,`*`被替換為`script.sh test.logs`,此時echo命令的引數是:`4567890 script.sh test.logs`
5. 最後,shell執行`echo 4567890 script.sh test.logs`,然後將echo命令執行的結果重定向到檔案中。
**Tips:**
> 萬用字元看起來有點像正則表示式,但是它與正則表示式不同的,不能相互混淆。可以把萬用字元理解為shell能夠處理的特殊字元。而且shell的萬用字元涉及的只有 "*, ?, [], {}" 這幾種。
>
> 萬用字元是shell自身支援的,而正則表示式需要相關工具的支援:grep,awk,vi,perl。在文字過濾工具裡,都是用正則表示式,比如像awk,sed等,**正則表示式是針對檔案的內容**。 **萬用字元多用於檔名或者路徑**上,比如查詢find,ls,cp等。
### 3. 解決方案
根據上面的分析可以知道,`$line`被替換後,萬用字元`*`再次被shell解析。哪有什麼辦法可以防止shell解析萬用字元呢?
1. 使用引用變數:`"$line"`,**在兩端加上引號**,這樣`"$line"`就變成了一個字串"4567890 *",而不是兩個單獨的字串。
2. 引用變數可防止分詞和萬用字元擴充套件(也就是shell解析萬用字元),並且可以防止在變數中包含空格、換行符、萬用字元等時造成指令碼中斷。
3. 在shell程式設計中總是使用**引用變數**的方式,這是一個良好又安全的編碼習慣。
### 4. 參考資料
1. [Reading asterisk character (*) from a file in bash](https://stackoverflow.com/questions/53751907/reading-asterisk-character-from-a-file-in-bash)
2. [When to wrap quotes around a shell variable?](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable)
3. [Security implications of forgetting to quote a variable in bash/POSIX shells](https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells)
4. [shellcheck.net-finds bugs in your shell scripts.](https://www.shellcheck.net/)
5. [Linux Shell 萬用字元、元字元、轉義符使用例項介紹](https://www.cnblogs.com/chengmo/archive/2010/10/17/18533