使用陣列的 for 迴圈是否比在簡單變數上使用欄位分割更好?

使用陣列的 for 迴圈是否比在簡單變數上使用欄位分割更好?

我有幾個開放的申請。跑步控制面板並將輸出透過管道傳輸到awk列出視窗 ID(不包括“黏性”視窗),如下所示:

$ wmctrl -l | awk ' !/-1/ { print $1 } '
0x00a00018
0x04800005
0x04e00005
0x04400003
0x05000003
0x0540002b
0x05a00012
0x05800002
0x05c00003
$ 

我可以將此輸出發送到控制面板關閉所有這些視窗:

  • 沒有需要保存內容的視窗和不需要回應的視窗將在不詢問我的情況下關閉,但是

  • 諸如具有未保存內容的編輯器或運行進程的終端的窗口將“優雅地”關閉:相應的應用程式將顯示一個窗口,允許我保存更改或放棄更改或通知我仍在運行的進程。

以下腳本指定了合適的快捷方式,可以正常工作:

#!/bin/bash

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')

for i in ${list[@]}
do
    wmctrl -i -a $i
    wmctrl -i -c $i
done

我發現(對我來說)更簡單的方法for i in $list也有效。

有什麼理由更喜歡其中一種嗎?


「黏性」和「優雅地」是來自 的術語man wmctrl

答案1

在你的腳本中$list與 相同${list[@]}

後者是數組語法,但在您的腳本中它是一個普通變數。


由於輸出項中沒有空格wmctl,因此不需要數組,使用$list就完全沒問題。


如果它曾是一個數組,$list只會是數組的第一項 (=> item1), ${list[@]}並將擴展到所有項目 (=> item1 item2 item3)。

但如果它真的是你真正想要的曾是數組"${list[@]}"(帶引號)擴展到"item1" "item2" "item3",因此它不會因空格而阻塞。


答案2

循環while通常比循環更適合for處理命令輸出,允許您直接處理行而不是將它們儲存在列表中或者大批。

在這種情況下,它允許您完全避免該awk命令:

wmctrl -l | while read -r id dt stuff; do 
  case $dt in 
    -1) continue
        ;; 
     *) echo wmctrl -i -a "$id"
        echo wmctrl -i -c "$id"
        ;; 
  esac
done

echo一旦您確信它正在做正確的事情,就刪除s。

正如評論中所述,xargs是另一種選擇 - 但是當您想對每個 執行多於一件事時,它會變得很棘手arg

答案3

回答原標題

原標題問「什麼類型的for迴圈比較好」。

對我自己來說,最好的方法就是最快的方法。要找出答案,請將該time命令新增到您的腳本或函數中。一些例子:

$ time du -s

real    0m0.002s
user    0m0.003s
sys     0m0.000s

$ time ls

real    0m0.004s
user    0m0.000s
sys     0m0.004s

不過,在測試之間刷新快取的緩衝區很重要:

如果兩個循環的速度大致相同,我會選擇可讀性最好的一個。

這個問題的範圍使得速度變得無關緊要,因為大部分時間都花在等待用戶輸入上,並且對於大多數人來說最多只打開 10 個視窗。


回答問題正文

其他答案則著重於重寫腳本,所以我也會給出我的兩美分。

該行:

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
  • 如果意圖是數組,則格式錯誤
  • list是通用的而不是描述性的

所以我會使用:

Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
  • 外部 () 群組告訴 bash/shell 內部的所有內容都是由空格分隔的陣列元素。
  • 我們所討論的是 Windows,因此它是一個描述性陣列名稱。
  • Windows 是複數形式,因此命名約定有助於識別它是一個陣列。

該行:

wmctrl -i -a $i
  • -i並且-a可以組合成-ia.
  • $i是非描述性的,我會用它$Window來代替。

有兩種方法可以編寫更短、更易讀的腳本,首先使用陣列:

#!/bin/bash
Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ' ) )
for Window in "${Windows[@]}" ; do wmctrl -ia $Window -c $Window ; done

第二個沒有陣列:

#!/bin/bash
Windows=$(wmctrl -l | awk ' !/-1/ { print $1 } ' )
for Window in $Windows ; do wmctrl -ia $Window -c $Window ; done

我更喜歡數組方法,因為我正在嘗試更多地了解它們並希望盡可能多地使用它們。然而,選擇權在於你。

答案4

您可以在沒有陣列的情況下進行管理。環境IFSto newline 將允許for循環行,然後您可以unset在循環內使用 IFS 而不會影響循環本身。

#!/bin/bash

IFS=$'\n'
for i in $(wmctrl -l); do
    unset IFS
    set -- $i
    (($2 > -1)) && wmctrl -i -a $1 -c $1
done

(重置位置參數是將一行分成多個欄位的巧妙技巧)。

如果你需要使用數組,你可以使用地圖檔案並利用回調函數創建類似於循環的東西。對於一小組迭代,使用更簡單的函數呼叫可能是一個優勢。

mapfile -c 1 -C 'f(){ set -- $@; (($3 >= 0)) && wmctrl -i -a $2 -c $2; }; f' -t < <(wmctrl -l)

(長版):

#!/bin/bash

f(){
    set -- $@
    if (($3 > -1)); then
        wmctrl -i -a $2 -c $2
    fi
}
mapfile -c 1 -C f -t < <(wmctrl -l)

相關內容