使用数组的 for 循环是否比在简单变量上使用字段拆分更好?

DK *_*ose 11 bash scripts

我打开了几个应用程序。运行wmctrl并将输出通过管道传送到awk列出窗口 ID(不包括“粘性”窗口),如下所示:

$ wmctrl -l | awk ' !/-1/ { print $1 } '
0x00a00018
0x04800005
0x04e00005
0x04400003
0x05000003
0x0540002b
0x05a00012
0x05800002
0x05c00003
$ 
Run Code Online (Sandbox Code Playgroud)

我可以将此输出发送到wmctrl以关闭所有这些窗口:

  • 没有需要保存内容的窗口和不需要响应的窗口将在不询问我的情况下关闭,但是

  • 诸如具有未保存内容的编辑器或运行进程的终端的窗口将“正常”关闭:相应的应用程序将显示一个窗口,允许我保存更改或放弃更改或通知我仍在运行的进程。

分配给合适的快捷方式的以下脚本有效:

#!/bin/bash

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

for i in ${list[@]}
do
    wmctrl -i -a $i
    wmctrl -i -c $i
done
Run Code Online (Sandbox Code Playgroud)

我发现更简单的(对我来说)for i in $list也有效。

有什么理由更喜欢一个吗?


“粘性”和“优雅”是来自man wmctrl.

pLu*_*umo 13

在您的脚本$list中与${list[@]}.

后者是数组语法,但在您的脚本中它是一个普通变量。


由于您的wmctl输出项中没有空格,因此您不需要数组,并且使用$list非常好。


如果它一个数组,则$list只会是数组的第一个项目 (=> item1), ${list[@]}并将扩展到所有项目 (=> item1 item2 item3)。

但是,如果它实际上一个数组,那么您真正想要的是"${list[@]}"(带引号)扩展到"item1" "item2" "item3",因此它不会因空格而窒息。


阅读


ste*_*ver 8

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
Run Code Online (Sandbox Code Playgroud)

echo一旦您对它做正确的事情感到高兴,请删除s。

正如评论中所述,xargs是另一种选择 - 但是当您想对每个arg.


Win*_*nix 5

回复原标题

原标题问“什么类型的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
Run Code Online (Sandbox Code Playgroud)

但是,在测试之间刷新缓存的缓冲区很重要:

如果两个循环的速度大致相同,我会选择可读性最好的循环。

这个问题的范围是使速度无关紧要,因为大部分时间都花在等待用户输入上,而且大多数人最多只能打开 10 个窗口。


回答问题的主体

其他答案侧重于重写脚本,所以我也会给我的两美分。

线路:

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
Run Code Online (Sandbox Code Playgroud)
  • 如果意图是数组,则格式错误
  • list 是通用的而不是描述性的

所以我会使用:

Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
Run Code Online (Sandbox Code Playgroud)
  • () 的外部集告诉 bash/shell 里面的所有内容都是由空格分隔的数组元素。
  • Windows 就是我们所说的,所以它是一个描述性的数组名称。
  • Windows 是复数形式,因此命名约定有助于识别它是一个数组。

线路:

wmctrl -i -a $i
Run Code Online (Sandbox Code Playgroud)
  • -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
Run Code Online (Sandbox Code Playgroud)

第二个没有数组:

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

我更喜欢数组方法,因为我正在尝试更多地了解它们并希望尽可能多地使用它们。然而,选择权在你。

  • @DKBose,我认为在这个问题的情况下,两种选择的时间是相同的(在测量精度范围内),所以结论是选择可读性最好的那个。 (2认同)

bac*_*c0n 5

您可以在没有阵列的情况下进行管理。将IFS设置为换行符将允许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
Run Code Online (Sandbox Code Playgroud)

(重置位置参数是将一行拆分为字段的巧妙技巧)。

如果您需要使用数组,您可以使用mapfile并利用回调函数来创建类似于循环的内容。对于一小组迭代,使用更简单的函数调用可能是一个优势。

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

(长版):

#!/bin/bash

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