Ich habe mehrere Anwendungen geöffnet. Läuftwmctrlund leitet die Ausgabe weiter anawklistet die Fenster-IDs (ausgenommen „klebrige“ Fenster) wie folgt auf:
$ wmctrl -l | awk ' !/-1/ { print $1 } '
0x00a00018
0x04800005
0x04e00005
0x04400003
0x05000003
0x0540002b
0x05a00012
0x05800002
0x05c00003
$
Ich kann diese Ausgabe senden anwmctrlum alle diese Fenster zu schließen:
Fenster ohne Inhalt, der gespeichert werden muss, und Fenster, die keine Antwort benötigen, werden ohne Rückfrage geschlossen, aber
Fenster wie die von Editoren mit nicht gespeichertem Inhalt oder Terminals, auf denen ein Prozess ausgeführt wird, werden „ordnungsgemäß“ geschlossen: Die jeweilige Anwendung zeigt ein Fenster an, in dem ich Änderungen speichern oder verwerfen kann oder das mich über einen noch laufenden Prozess informiert.
Das folgende Skript, dem entsprechenden Tastaturkürzel zugewiesen, funktioniert:
#!/bin/bash
list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
for i in ${list[@]}
do
wmctrl -i -a $i
wmctrl -i -c $i
done
Ich habe festgestellt, dass die (für mich) einfachere Variante for i in $list
auch funktioniert.
Gibt es einen Grund, das eine dem anderen vorzuziehen?
„klebrig“ und „anmutig“ sind Begriffe aus man wmctrl
.
Antwort1
In Ihrem Skript $list
ist es dasselbe wie ${list[@]}
.
Letzteres ist Array-Syntax, in Ihrem Skript ist es jedoch eine normale Variable.
wmctl
Da Ihre Ausgabeelemente keine Leerzeichen enthalten , benötigen Sie kein Array und die Verwendung $list
ist völlig ausreichend.
Wenn esWarEin Array $list
wäre beispielsweise nur das erste Element des Arrays (=> item1
) und
${list[@]}
würde sich auf alle Elemente erstrecken (=> item1 item2 item3
).
Aber was wolltest du wirklich, wenn es tatsächlichWarEin Array ist "${list[@]}"
(mit Anführungszeichen), das sich bis erstreckt "item1" "item2" "item3"
, sodass es nicht an Leerzeichen erstickt.
(Lesen)
Antwort2
Für die Verarbeitung von Befehlsausgaben ist eine while
Schleife häufig besser geeignet als eine herkömmliche for
Schleife, da Sie Zeilen so direkt verarbeiten können, statt sie in einer Liste zu speichern.oder-Array.
In diesem Fall können Sie den awk
Befehl vollständig vermeiden:
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
Entfernen Sie das echo
„s“, sobald Sie zufrieden sind, dass es das Richtige tut.
Wie in den Kommentaren erwähnt, xargs
ist eine weitere Option – es wird aber schwierig, wenn Sie mit jedem mehr als eine Sache machen möchten arg
.
Antwort3
Antwort zum Originaltitel
Im Originaltitel wurde gefragt: „Welche Art von For-Schleife ist besser?“
Für mich ist die beste Methode die schnellste. Um das herauszufinden, stellen Sie den time
Befehl Ihrem Skript oder Ihrer Funktion voran. Einige Beispiele:
$ time du -s
real 0m0.002s
user 0m0.003s
sys 0m0.000s
$ time ls
real 0m0.004s
user 0m0.000s
sys 0m0.004s
Es ist jedoch wichtig, zwischengespeicherte Puffer zwischen den Tests zu leeren:
Wenn zwei Schleifen ungefähr gleich schnell sind, wähle ich die mit der besten Lesbarkeit.
Allerdings ist die Geschwindigkeit angesichts des Umfangs dieser Frage irrelevant, da die meiste Zeit mit Warten auf Benutzereingaben verbracht wird und die meisten Leute nur maximal 10 Fenster geöffnet haben.
Antwort auf den Fragetext
In anderen Antworten geht es um die Neufassung des Skripts, daher werde ich auch meinen Senf dazu geben.
Die Linie:
list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
- ist fehlerhaft, wenn es sich um ein Array handeln soll
list
ist allgemein und nicht beschreibend
Ich würde also verwenden:
Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
- Der äußere Satz von () teilt Bash/Shell mit, dass alles darin ein durch Leerzeichen abgegrenztes Array-Element ist.
- Wir sprechen hier von Fenstern, es handelt sich also um einen beschreibenden Array-Namen.
- „Windows“ ist Plural, daher hilft die Namenskonvention dabei, zu erkennen, dass es sich um ein Array handelt.
Die Linie:
wmctrl -i -a $i
-i
und-a
können zu kombiniert werden-ia
.$i
ist nicht beschreibend, ich würde$Window
stattdessen verwenden.
Es gibt zwei Möglichkeiten, ein kürzeres, besser lesbares Skript zu schreiben, zunächst mit einem Array:
#!/bin/bash
Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ' ) )
for Window in "${Windows[@]}" ; do wmctrl -ia $Window -c $Window ; done
zweite ohne Array:
#!/bin/bash
Windows=$(wmctrl -l | awk ' !/-1/ { print $1 } ' )
for Window in $Windows ; do wmctrl -ia $Window -c $Window ; done
Ich bevorzuge die Array-Methode, weil ich mehr darüber lernen und sie so oft wie möglich verwenden möchte. Die Wahl liegt jedoch bei Ihnen.
Antwort4
Sie können auch ohne Array auskommen. EinstellungIFSzum Umwandeln von Zeilen in eine neue Zeile ermöglicht for
das Schleifen von Zeilen. Sie können dann unset
das IFS innerhalb der Schleife verwenden, ohne die Schleife selbst zu beeinflussen.
#!/bin/bash
IFS=$'\n'
for i in $(wmctrl -l); do
unset IFS
set -- $i
(($2 > -1)) && wmctrl -i -a $1 -c $1
done
(Zurücksetzen derPositionsparameterist ein guter Trick, um eine Zeile in Felder aufzuteilen).
Wenn Sie ein Array verwenden müssen, können Sie verwendenMap-Dateiund nutzen Sie die Callback-Funktion, um etwas zu erstellen, das einer Schleife ähnelt. Für eine kleine Anzahl von Iterationen kann es von Vorteil sein, den einfacheren Funktionsaufruf zu verwenden.
mapfile -c 1 -C 'f(){ set -- $@; (($3 >= 0)) && wmctrl -i -a $2 -c $2; }; f' -t < <(wmctrl -l)
(lange Version):
#!/bin/bash
f(){
set -- $@
if (($3 > -1)); then
wmctrl -i -a $2 -c $2
fi
}
mapfile -c 1 -C f -t < <(wmctrl -l)