I have several applications open. Running wmctrl and piping the output to awk lists the window IDs (excluding "sticky" windows) like this:
$ wmctrl -l | awk ' !/-1/ { print $1 } '
0x00a00018
0x04800005
0x04e00005
0x04400003
0x05000003
0x0540002b
0x05a00012
0x05800002
0x05c00003
$
I can send this output to wmctrl to close all these windows:
windows without content that needs to be saved and windows that don't need a response will be closed without asking me but
windows such as those of editors with unsaved content or terminals running a process will be closed "gracefully": the respective application will present a window allowing me to save changes or discard changes or to inform me of a process that's still running.
The following script, assigned to a suitable shortcut, works:
#!/bin/bash
list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
for i in ${list[@]}
do
wmctrl -i -a $i
wmctrl -i -c $i
done
I found that the simpler (to me) for i in $list
also works.
Is there any reason to prefer one over the other?
"sticky" and "gracefully" are terms from man wmctrl
.
In your script
$list
is the same as${list[@]}
.The latter is array syntax, but in your script it is a normal variable.
As you have no whitespace in your
wmctl
output items, you don't need an array, and using$list
is perfectly fine.If it was an array,
$list
would only be the first item of the array (=>item1
) and${list[@]}
would extend to all items (=>item1 item2 item3
).But what you really wanted if it actually was an array is
"${list[@]}"
(with quotes) that extends to"item1" "item2" "item3"
, so it would not choke on whitespace.(Read)
A
while
loop is often a better fit than afor
loop for processing command output, allowing you to process lines directly rather than storing them in a list or array.In this case, it allows you to avoid the
awk
command altogether:Remove the
echo
s once you are happy that it is doing the right thing.As noted in comments,
xargs
is another option - but it gets tricky when you want to do more than one thing with eacharg
.Answer to original title
The original title asked "what type of for loop is better".
For myself, the best method is the fastest one. To find out prepend the
time
command to your script or function. Some examples:It is important to flush cached buffers in-between tests though:
If two loops are about the same in speed, I'll pick the one with best readability.
The scope of this question is makes speed irrelevant though because most of the time is spent waiting for user input and there are only a maximum of 10 windows open for most people.
Answer to body of question
Other answers focus on rewriting the script so I'll give my two cents worth too.
The line:
list
is generic and not descriptiveSo I would use:
The line:
-i
and-a
can be combined into-ia
.$i
is non-descriptive I would use$Window
instead.There are two ways of writing a shorter more readable script, first with an array:
second without an array:
I prefer the array method because I'm trying to learn more about them and want to use them as much as possible. The choice is yours however.
You can manage without an array. Setting IFS to newline will allow
for
to loop lines, you can thenunset
the IFS inside the loop without affecting the loop itself.(resetting the positional parameters is a neat trick to split a line in to fields).
if you need to use an array you can use mapfile and take advantage of the callback function to create something similar to a loop. For a small set of iterations it can be an advantage to use the simpler function call.
(long version):