
我正在編寫一個腳本,它將文件中每行的前 3 個字母(透過使用 cut 來獲取它們)與數組中的字串進行比較。我已經環顧四周,但我找到的解決方案不適用於我的系統。
現在看起來像這樣:
weekdays=([Mon]=1 [Tue]=1 [Wed]=1 [Thu]=1 [Fri]=1 [Sat]=1 [Sun]=1)
input="/Foo/Bar.log"
while read -r line
do
cutline="$(echo ${line} | cut -c 1-3"
if [[ ${weekdays["$cutline"]} ]]
then
echo "Match"
else
echo "No Match"
fi
done < ${input}
該線被正確切斷,但測試期間的某些內容會返回誤報,因為無論前 3 個字母是什麼,它都會返回「匹配」。
當我使用 -x 檢查腳本時,它向我展示了,而不是它使用的實際測試
[[ -n 1 ]]
當我用表達式測試它時,[ ]
它顯示了1
它是否檢查數組中的每個字符而不僅僅是整個單詞,或者是否還有其他問題?
如果沒有問題,是否有另一種方法可以將一行的前 3 個字母與數組中的所有字母進行比較,然後再繼續下一個字母?
附帶說明:我確實正在運行 Bash 4,所以關聯數組應該可以工作
答案1
基本錯誤是您實際上並未聲明關聯數組:
$ weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
$ echo ${weekdays[@]}
1
$ echo ${weekdays[0]}
1
$ echo ${weekdays[2]}
$
我不完全確定 bash 如何處理它以及為什麼它只需要一個1
,但我確信這不是一個關聯數組。正如man bash
(強調我的)中所解釋的:
如果使用語法 name[下標]=value 指派任何變量,則會自動建立索引數組。下標被視為必須計算為數字的算術表達式。若要明確宣告索引數組,請使用declare -a name(請參閱下方的SHELL BUILTIN指令)。聲明 -a name[下標] 也被接受;下標被忽略。
關聯數組是使用聲明 -A 名稱建立的。
因此,請嘗試這樣做,它將按您的預期工作:
declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
也就是說,您的腳本比您需要的要複雜一些。這是使用相同方法的更簡單的版本:
#!/bin/bash
declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
input="/Foo/Bar.log"
cut -c 1-3 "$input" | while read -r line; do
if [[ ${weekdays["$line"]} ]]
then
echo "Match : $cutline : ${weekdays[$line]}"
else
echo "No Match"
fi
done
雖然我可能會這樣做:
#!/bin/bash
cut -c 1-3 "$1" | while read -r line; do
case $line in
"Mon"|"Tue"|"Wed"|"Thu"|"Fri"|"Sat"|"Sun")
echo yes;;
*)
echo no;;
esac
done
然後,使用目標檔案名稱作為參數來執行腳本:
script.sh /Foo/Bar.log"
答案2
我會使用一次文本處理工具的呼叫來處理文本,不是每行輸入都有幾個工具:
awk -v 'weekday=(Mon|Tue|Wed|Thu|Fri|Sat|Sun)' '
{print ($0 ~ "^" weekday ? "" : "No ") "Match"}' < "$input"
如果您需要為輸入的每一行運行特定的應用程序,則可以使用循環,但如果只是文字處理(例如將行輸出到某個文件),則awk
可以這樣做:
awk -v 'weekday=Mon|Tue|Wed|Thu|Fri|Sat|Sun' '
(day = substr($0, 1, 3)) ~ weekday {
print substr($0, 4) > day ".txt"
} < "$input"