如何使 mksh for 循環從 1 變為 N
所以我試著這樣做
for x in {1..7000} do echo $x done
輸出是
{1..7000}
我還了解到 C 風格的 for 循環在 mksh 中不起作用
我做了一些Google搜尋,但主要是關於 ksh 的資訊,而不是 mksh
那麼在 mksh 中迭代一個範圍的正確方法是什麼?
**注意:**我對創建迭代和類 c 行為的正確方法更感興趣,而不是列印字元串。
所以,首先,正如這裡簡要討論的那樣,常見的形式……
for arg in {brace..expanded..set}; do ...
…在我看來,是 shell 程式錯誤的典範。它在循環中生成一個公式集,將每個結果儲存在一個分隔數組中,然後將另一個循環傳遞給該數組作為要迭代的列表。
作為一般規則,大括號擴展在程式上下文中並不是特別有用,因為所有 shell 程式都以一種或另一種方式類似於宏,並且大括號擴展已經是一個宏。雖然大括號擴展*(在我看來也是)一個非常有用*的互動式 shell 功能正是因為它是一個宏,否則它可能是浪費的。
大括號擴展也是不可移植的(閱讀:POSIX-portable)。據我所知,您通常可以依靠它在 、 、 的目前版本中工作,
zsh
但不能在其他地方工作。即使在那些 shell 中,也可以使用 shell 選項禁用它,因此在 shell 函式上下文中不能絕對依賴它,儘管它的可用性通常可以在. 在它們之間,上述每個 shell 都需要不同類型的測試,因此如果您打算在函式中使用它並想要對其進行測試,請查看目標 shell 的手冊以了解具體資訊。bash``yash``ksh93``$-``set +o
在其他 shell 中,您可以獲得幾乎相同的行為 - 除了必須呼叫外部執行檔來生成集合 - 通過將命令替換的輸出拆分為
$IFS
. 在seq
程序可用的地方,這很容易證明,例如……unset IFS ### ^ensure $IFS default behavior for arg in $(seq 7000) do printf %d\\n "$arg" done
…或者…
unset IFS printf %d\\n $(seq 7000)
…但是,正如所寫的那樣,除了對…不利之外,兩者都沒有提供任何東西
seq 7000
仍然 - 對於第一個範例 - 該方法與大括號擴展版本幾乎相同。在這兩種情況下,其他一些函式或程序會生成一個完整的參數列表,
for
然後控制結構會對其進行迭代。但是,在後一種情況下, (對於除ksh93
.seq``$IFS
$IFS
順便說一下,根據所使用的外殼,分裂的速度非常不同。bash
,例如,在簡單的$IFS
拆分性能方面通常很糟糕,而dash
- 取決於拆分字元串的行長 - 通常可以像其他一些 shell 進行大括號擴展一樣快地做到這一點。但是,無論您使用大括號擴展還是
$IFS
拆分,最終結果都是您將執行一些循環來生成公式列表,然後再執行另一個循環來迭代該列表。相反,您可能已經 - 並且可能應該 - 做的是在生成列表的公式的應用程序上執行一個循環,盡快處理使用的迭代器,並測試每次迭代的結果滿意的結果。這就是你會在 C 中做的事情,比如……for ( x = 0; x < 10; x++ ){ ...
這實際上也與您可以在
bc
. 它很像方便的語法形式,可以用在ksh93
或zsh
類似的地方bash
:for (( x=0; x<10; x++ )); do ...
但是,這也不是可移植的*(再次閱讀:POSIX)*。相反,您可以便攜地執行以下操作:
iterator=$(( start_value - interval )) while [ "$(( iterator += interval ))" -le "$end_value" ]; do ...
…或者…
iterator=$(( start_value - interval )) while [ "$(( ( iterator += interval ) <= end_value ))" -ne 0 ]; do ...
POSIX為 shell 算術擴展和 C 語言算術運算指定了接近特性奇偶性的東西——即涉及整數的地方。因此,您通常只需一次擴展即可方便且便攜地迭代甚至複雜的公式 - 甚至可以同時同步地迭代多個變數。
另一方面,這樣的結構可能會讓你很難對抗另一種 C 語言與 shell 語言的不平等。Shell 讀取和寫入幾乎總是字面意義上
read()
的 s 和write()
s - 每個都是對自身的系統呼叫 - 並且沒有可移植的類似fread()
,fwrite()
和/或setbuf()
shell 語法。因此,任何在每次迭代中執行任何此類操作的循環——即使它只使用內置函式——也必然會導致性能下降。不過,您有時可以在 shell 腳本中以串列或併行方式生成和工作整個集合之間達成一種愉快的平衡。
set 0 1 2 3 4 5 6 7 8 9 for n do printf "$n%d\n" "$@"; done
這
write()
在 10 次迭代中執行了 10 秒,並列印了一個從 00 到 99 的新行分割序列。這是一個小例子 - 仍然很浪費,因為 30 字節write()
幾乎不是 3 字節的顯著改進,write()
但它是那種類型的證明您可能使用的方法。正如我之前提到的,shell 程式語言非常類似於宏,因為一切都是字元串。因此,如果您可以將您的目標集分解為更小、更易於管理的集合 - 然後在循環上下文中反複呼叫它們以最終生成您的目標集,您通常可以獲得更高性能的結果。這是一個更複雜的類似宏的範例:
sh -c ' i=0 _i=-25 set "$1" "$1" "$1" "$1" "$1" for n do eval " printf %b%d $@ $@ $@ $@ $@" done' -- \ '"\01$((1+(i<(i+=!(_i=(_i+=25)%275)))))" "$i$_i"'
每次迭代 5 次
write()
- 平均 - 大約 100 個字節並列印:10 125 150 175 1100 1125 1150 1175 1200 1225 1250 20 225 250 275 2100 2125 2150 2175 2200 2225 2250 30 325 350 375 3100 3125 3150 3175 3200 3225 3250 40 425 450 475 4100 4125 4150 4175 4200 4225 4250 50 525 550 575 5100 5125 5150 5175 5200 5225 5250 60 625 650 675 6100 6125 6150 6175 6200 6225 6250 70 725 750 775 7100 7125 7150 7175 7200 7225 7250 80 825 850 875 8100 8125 8150 8175 8200 8225 8250 90 925 950 975 9100 9125 9150 9175 9200 9225 9250 100 1025 1050 1075 10100 10125 10150 10175 10200 10225 10250 110 1125 1150 1175 11100 11125 11150 11175 11200 11225 11250 120 1225 1250 1275