本文内容概要
參數擴充
0m2.828s | printf -v i '%s ' {1..1000000}; time { : $i; } |
0m6.885s | i=( {1..1000000} ); time { : "${i[@]}"; } |
這個壓測結果涉及了兩種在Shell腳本中擴充多個參數的方法,即使用 printf 指令和使用花括号文法。下面對每個方法的表現進行分析:
- printf -v i '%s ' {1..1000000}; time { : $i; }
該方法使用 printf 指令将數字清單轉換為一個字元串,并将其存儲在變量 i 中。在這個測試中,使用了 time 指令來記錄執行時間,并将空指令 : 作為測試目标。該方法的執行時間為 0m2.828s。
- i=( {1..1000000} ); time { : "${i[@]}"; }
該方法使用花括号文法 {1..1000000} 将數字清單轉換為一個數組,并将其存儲在變量 i 中。在這個測試中,使用了 time 指令來記錄執行時間,并将空指令 : 作為測試目标。該方法的執行時間為 0m6.885s。
結論:使用 printf 指令将數字清單轉換為字元串,并使用引号括起來的變量名稱作為測試目标的方法表現最佳,執行時間最短。另外,在使用變量時,盡可能地使用引号來避免意外的結果或錯誤。使用花括号文法也是一種不錯的方法,但其執行時間較長,可能會在處理大量資料時影響性能。
Testing測試塊
0m1.099s | time for i in {1..100000}; do [[ -d . ]]; done |
0m2.110s | time for i in {1..100000}; do [ -d . ]; done |
3m47.509s | time for i in {1..100000}; do /bin/test -d .; done |
這個壓測結果涉及了三種判斷目前目錄是否為一個目錄的方法,即使用 [[ -d . ]]、[ -d . ] 和 /bin/test -d .。下面對每個方法的表現進行分析:
- time for i in {1..100000}; do [[ -d . ]]; done
該方法使用雙方括号 [[ ]] 和 for 循環來判斷目前目錄是否為一個目錄。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m1.099s。
需要注意的是,雙方括号 [[ ]] 是一個關鍵字,其具有特殊的解析規則,可以用于比較複雜的條件測試。
- time for i in {1..100000}; do [ -d . ]; done
該方法使用單方括号 [ ] 和 for 循環來判斷目前目錄是否為一個目錄。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m2.110s。
需要注意的是,單方括号 [ ] 是一個内置指令,其解析規則比雙方括号 [[ ]] 稍微簡單一些。
- time for i in {1..100000}; do /bin/test -d .; done
該方法使用 /bin/test 指令和 for 循環來判斷目前目錄是否為一個目錄。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 3m47.509s。
需要注意的是,/bin/test 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度比内置指令和關鍵字慢得多。
結論:使用雙方括号 [[ ]] 的方法表現最佳,執行時間最短。使用單方括号 [ ] 的方法也表現不錯,但是速度較慢。使用外部指令 /bin/test 的方法速度最慢,不建議使用。
字元串操作
0m0.149s | path=/foo/bar; time for i in {1..10000}; do : "${path##*/}"; done |
0m19.284s | path=/foo/bar; time for i in {1..10000}; do basename "$path" >/dev/null; done |
0m21.289s | path=/foo/bar; time for i in {1..10000}; do expr "$path" : './.)' >/dev/null; done |
0m24.789s | path=/foo/bar; time for i in {1..10000}; do sed 's,.*/,,' <<< "$path" >/dev/null; done |
這個壓測結果涉及了四種從一個路徑中提取檔案名的方法,即使用參數擴充 ${path##*/}、basename 指令、expr 指令和 sed 指令。下面對每個方法的表現進行分析:
- path=/foo/bar; time for i in {1..10000}; do : "${path##*/}"; done
該方法使用參數擴充 ${path##*/} 和 for 循環來從路徑中提取檔案名。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.149s。
需要注意的是,${path##*/} 是一種快速提取檔案名的方法,其可以盡可能地避免啟動新的程序。
- path=/foo/bar; time for i in {1..10000}; do basename "$path" >/dev/null; done
該方法使用 basename 指令和 for 循環來從路徑中提取檔案名。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m19.284s。
需要注意的是,basename 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度較慢。
- path=/foo/bar; time for i in {1..10000}; do expr "$path" : '.*/.*\)' >/dev/null; done
該方法使用 expr 指令和 for 循環來從路徑中提取檔案名。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m21.289s。
需要注意的是,expr 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度較慢。
- path=/foo/bar; time for i in {1..10000}; do sed 's,.*/,,' <<< "$path" >/dev/null; done
該方法使用 sed 指令和 for 循環來從路徑中提取檔案名。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m24.789s。
需要注意的是,sed 指令是一個外部指令,每次調用都需要啟動一個新的程序,是以其執行速度較慢。
結論:在這些測試中,使用 ${path##*/} 的方法表現最佳,執行時間最短。使用 basename、expr 和 sed 指令的方法都需要啟動新的程序,是以其執行速度較慢,不建議使用。
參數疊代
0m0.239s | set -- {1..100000}; time for x; do <(); done |
0m0.298s | set -- {1..100000}; time for x; do :; done |
0m12.759s | set -- {1..100000}; n=1; time while ((${!n+n++})); do :; done |
0m15.700s | set -- {1..100000}; n=1; time while [[ ${!n+_} ]]; do ((++n)); done |
0m18.560s | set -- {1..100000}; time while shift; do :; done |
0m38.831s | set -- {1..100000}; n=1; time while ((n++<$#)); do :; done |
這個壓測結果涉及了五種空操作的方法,即使用空的程序替換 <()、冒号指令 :、循環、移動變量和計算參數個數。下面對每個方法的表現進行分析:
- set -- {1..100000}; time for x; do <(); done
該方法使用空的程序替換 <() 和 for 循環來進行空操作。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m0.239s。
需要注意的是,空的程序替換 <() 是一種快速進行空操作的方法,其可以盡可能地避免啟動新的程序。
- set -- {1..100000}; time for x; do :; done
該方法使用冒号指令 : 和 for 循環來進行空操作。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m0.298s。
需要注意的是,冒号指令 : 是一種常用的進行空操作的方法,其執行速度比較快。
- set -- {1..100000}; n=1; time while ((${!n+n++})); do :; done
該方法使用循環和參數擴充 ${!n} 和 ${n++} 來進行空操作。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m12.759s。
需要注意的是,使用參數擴充 ${!n} 和 ${n++} 的方法在循環中進行大量的變量操作,是以其執行速度較慢。
- set -- {1..100000}; n=1; time while [[ ${!n+_} ]]; do ((++n)); done
該方法使用循環和參數擴充 ${!n} 來進行空操作。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m15.700s。
需要注意的是,使用參數擴充 ${!n} 的方法在循環中進行大量的變量操作,是以其執行速度較慢。
- set -- {1..100000}; time while shift; do :; done
該方法使用循環和 shift 指令來進行空操作。在這個測試中,使用了 time 指令來記錄執行時間,循環了多次,但執行時間不穩定。該方法的執行時間可能會受到緩存的影響,重複移動大參數清單可能會導緻緩存未命中。執行時間在不同的測試中可能會有很大的差别。
需要注意的是,使用 shift 指令的方法在循環中進行大量的變量操作,是以其執行速度較慢。此外,該方法的執行時間可能會受到緩存的影響。
- set -- {1..100000}; n=1; time while ((n++<$#)); do :; done
該方法使用循環和參數擴充 $# 來進行空操作。在這個測試中,使用了 time 指令來記錄執行時間,循環了100000次。該方法的執行時間為 0m38.831s。
需要注意的是,使用參數擴充 $# 的方法需要在每次循環中計算參數的個數,是以其執行速度較慢。
結論:在這些測試中,使用空的程序替換 <() 的方法表現最佳,執行時間最短。使用冒号指令 : 也是一種執行速度較快的方法。其他方法的執行速度較慢,不建議使用。
不同的shell解釋器
0m0.021s | time dash -c ':' -- {1..10000} |
0m0.024s | time ksh -c ':' -- {1..10000} |
0m0.027s | time zsh -c ':' -- {1..10000} |
0m0.217s | time bash -c ':' -- {1..10000} |
這個壓測結果涉及了四種 shell 解釋器執行空指令 : 的方法,即使用 dash、ksh、zsh 和 bash。下面對每個方法的表現進行分析:
- time dash -c ':' -- {1..10000}
該方法使用 dash shell 解釋器執行空指令 : 和 -c 選項來進行壓測。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.021s。
需要注意的是,dash 是一種輕量級的 shell 解釋器,其啟動速度比較快,是以在執行空指令時表現較優。
- time ksh -c ':' -- {1..10000}
該方法使用 ksh shell 解釋器執行空指令 : 和 -c 選項來進行壓測。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.024s。
需要注意的是,ksh 是一種比較古老的 shell 解釋器,其啟動速度比較快,是以在執行空指令時表現較優。
- time zsh -c ':' -- {1..10000}
該方法使用 zsh shell 解釋器執行空指令 : 和 -c 選項來進行壓測。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.027s。
需要注意的是,zsh 是一種功能比較強大的 shell 解釋器,其啟動速度較快,是以在執行空指令時表現較優。
- time bash -c ':' -- {1..10000}
該方法使用 bash shell 解釋器執行空指令 : 和 -c 選項來進行壓測。在這個測試中,使用了 time 指令來記錄執行時間,循環了10000次。該方法的執行時間為 0m0.217s。
需要注意的是,bash 是一種功能比較強大的 shell 解釋器,其啟動速度較慢,特别是在使用長參數清單時,會導緻啟動時間更長。但是,一旦 bash 啟動完成,其執行空指令的速度與其他解釋器相當。
結論:dash、ksh 和 zsh 的啟動速度比較快,是以在執行空指令時表現較優。bash 的啟動速度較慢,但一旦啟動完成,其執行空指令的速度與其他解釋器相當。
總結
本文基于多個次元分别針對shell在不同場景下的多種使用方式進行了測試驗證,提供了很多測試驗證資料友善大家使用的時候注意。shell是一個非常靈活的解釋性腳本語言,其能夠動态調用外部程序執行任務,提供了靈活多變的執行方式,但是同時也埋了很多使用層面的坑,這也是為啥編寫《shell腳本程式設計最佳實踐》專欄的初衷,就是幫助大家快速了解到shell的各種坑通過該專欄作為一個避坑指南,幫助大家了解各種生産環境實踐,幫大家減少困惑。目前專欄還在持續更新中,優惠力度較大歡迎趁優惠購買。