今天分享一個有關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