管道、轉移或參數擴展是否更有效?
我試圖找到最有效的方法來遍歷某些值,這些值是在空格分隔的單詞列表中彼此相距一致數量的值(我不想使用數組)。例如,
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
所以我希望能夠只遍歷列表並且只訪問 1、5、6、9 和 15。
***編輯:***我應該明確表示我試圖從列表中獲取的值在格式上不必與列表的其餘部分不同。使它們與眾不同的僅僅是它們在列表中的位置(在這種情況下,位置 1,4,7…)。所以列表可能是
1 2 3 5 9 8 6 90 84 9 3 2 15 75 55
,但我仍然想要相同的數字。而且,假設我不知道列表的長度,我希望能夠做到這一點。目前我想到的方法有:
方法一
set $list found=false find=9 count=1 while [ $count -lt $# ]; do if [ "${@:count:1}" -eq $find ]; then found=true break fi count=`expr $count + 3` done
方法二
set list found=false find=9 while [ $# ne 0 ]; do if [ $1 -eq $find ]; then found=true break fi shift 3 done
方法 3 我很確定管道使這是最糟糕的選擇,但出於好奇,我試圖找到一種不使用 set 的方法。
found=false find=9 count=1 num=`echo $list | cut -d ' ' -f$count` while [ -n "$num" ]; do if [ $num -eq $find ]; then found=true break fi count=`expr $count + 3` num=`echo $list | cut -d ' ' -f$count` done
那麼什麼是最有效的,或者我錯過了一個更簡單的方法?
很簡單
awk
。這將為您提供任意長度輸入的每四個欄位的值:$ awk -F' ' '{for( i=1;i<=NF;i+=3) { printf( "%s%s", $i, OFS ) }; printf( "\n" ) }' <<< $list 1 5 6 9 15
這可以利用內置
awk
變數,例如NF
(記錄中的欄位數),並進行一些簡單for
的循環以沿欄位進行迭代,從而為您提供所需的欄位,而無需提前知道會有多少。或者,如果您確實只想要範例中指定的那些特定欄位:
$ awk -F' ' '{ print $1, $4, $7, $10, $13 }' <<< $list 1 5 6 9 15
至於關於效率的問題,最簡單的方法是測試這個或你的其他每個方法,並用
time
它來顯示需要多長時間;您還可以使用諸如strace
查看系統呼叫流程之類的工具。外觀的用法time
如下:$ time ./script.sh real 0m0.025s user 0m0.004s sys 0m0.008s
您可以比較不同方法之間的輸出,以查看哪種方法在時間上最有效;其他工具可用於其他效率指標。
- 軟體優化的第一條規則:不要。
在您知道程序的速度是一個問題之前,沒有必要考慮它有多快。如果您的列表大約有這個長度或只有大約 100-1000 個項目長,您可能甚至不會注意到它需要多長時間。您可能會花更多時間考慮優化而不是差異。
- 第二條規則:測量。
這是找出答案的可靠方法,也是為您的系統提供答案的方法。尤其是貝殼,有很多,而且它們並不完全相同。一個外殼的答案可能不適用於您的外殼。
在較大的程序中,分析也在這裡。最慢的部分可能不是你認為的那個。
- 三、shell腳本優化的第一條規則:不要使用shell。
是的,真的。許多 shell 的速度並不快(因為不必啟動外部程序),它們甚至可能每次都重新解析原始碼的行。
改用 awk 或 Perl 之類的東西。在我做的一個微不足道的微基準測試中,
awk
執行一個簡單的循環(沒有 I/O)比任何常見的 shell 快幾十倍。但是,如果您確實使用 shell,請使用 shell 的內置函式而不是外部命令。在這裡,您使用
expr
的不是我在系統上找到的任何 shell 中內置的,但可以用標準算術擴展替換。例如i=$((i+1))
,而不是i=$(expr $i + 1)
增加i
. 您cut
在上一個範例中的使用也可以用標準參數擴展替換。另請參閱:為什麼使用 shell 循環處理文本被認為是不好的做法?
步驟 #1 和 #2 應該適用於您的問題。