Mksh

如何使 mksh for 循環從 1 變為 N

  • April 28, 2015

所以我試著這樣做

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. 它很像方便的語法形式,可以用在ksh93zsh類似的地方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

引用自:https://unix.stackexchange.com/questions/198985