今天分享一個有關shell編程中由通配符引起的問題,
1. 問題代碼
cat test.logs
4567890 *
##*************************************##
rtyuio**tyuio432
##*************************************##
*rtyuiop*2* yuiop
##*************************************##
rtyuiop(3 * 4)iuytr
##*************************************##
8765432
cat script.sh
#!/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腳本的結果:

從圖片上紅框部分可以看到:
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執行腳本的原理:
- shell讀取整個腳本檔案,然后從上到下依次執行每一行
- 假設當前line=4567890 *,當shell執行
echo $line >> $i.txt時- 首先,shell負責替換
$line的值為:4567890 *,此時代碼是:echo 4567890 * >> $i.txt - 然后,shell在執行echo命令之前,檢查命令的引數中是否有通配符(PS:此時echo的引數:4567890 *)
- 很明顯,
*是通配符,shell負責決議通配符,shell會將通配符當作路徑或檔案名在磁盤上搜尋可能的匹配:若符合要求的匹配存在,則進行替換(路徑擴展);否則就將該通配符作為一個普通字符引數傳遞給echo,然后再由echo進行處理, - 決議完通配符后,
*被替換為script.sh test.logs,此時echo命令的引數是:4567890 script.sh test.logs - 最后,shell執行
echo 4567890 script.sh test.logs,然后將echo命令執行的結果重定向到檔案中,
- 首先,shell負責替換
Tips:
通配符看起來有點像正則運算式,但是它與正則運算式不同的,不能相互混淆,可以把通配符理解為shell能夠處理的特殊字符,而且shell的通配符涉及的只有 "*, ?, [], {}" 這幾種,
通配符是shell自身支持的,而正則運算式需要相關工具的支持:grep,awk,vi,perl,在文本過濾工具里,都是用正則運算式,比如像awk,sed等,正則運算式是針對檔案的內容, 通配符多用于檔案名或者路徑上,比如查找find,ls,cp等,
3. 解決方案
根據上面的分析可以知道,$line被替換后,通配符*再次被shell決議,那有什么辦法可以防止shell決議通配符呢?
- 使用參考變數:
"$line",在兩端加上引號,這樣"$line"就變成了一個字串"4567890 *",而不是兩個單獨的字串, - 參考變數可防止分詞和通配符擴展(也就是shell決議通配符),并且可以防止在變數中包含空格、換行符、通配符等時造成腳本中斷,
- 在shell編程中總是使用參考變數的方式,這是一個良好又安全的編碼習慣,
4. 參考資料
- Reading asterisk character (*) from a file in bash
- When to wrap quotes around a shell variable?
- Security implications of forgetting to quote a variable in bash/POSIX shells
- shellcheck.net-finds bugs in your shell scripts.
- Linux Shell 通配符、元字符、轉義符使用實體介紹
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/2140.html
標籤:其他
上一篇:JS(二)
下一篇:陣列----二維陣列中的查找
