Ist eine For-Schleife mit Arrays besser als die Verwendung der Feldaufteilung für eine einfache Variable?

Ist eine For-Schleife mit Arrays besser als die Verwendung der Feldaufteilung für eine einfache Variable?

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 $listauch funktioniert.

Gibt es einen Grund, das eine dem anderen vorzuziehen?


„klebrig“ und „anmutig“ sind Begriffe aus man wmctrl.

Antwort1

In Ihrem Skript $listist es dasselbe wie ${list[@]}.

Letzteres ist Array-Syntax, in Ihrem Skript ist es jedoch eine normale Variable.


wmctlDa Ihre Ausgabeelemente keine Leerzeichen enthalten , benötigen Sie kein Array und die Verwendung $listist völlig ausreichend.


Wenn esWarEin Array $listwä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 whileSchleife häufig besser geeignet als eine herkömmliche forSchleife, da Sie Zeilen so direkt verarbeiten können, statt sie in einer Liste zu speichern.oder-Array.

In diesem Fall können Sie den awkBefehl 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, xargsist 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 timeBefehl 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
  • listist 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
  • -iund -akönnen zu kombiniert werden -ia.
  • $iist nicht beschreibend, ich würde $Windowstattdessen 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 fordas Schleifen von Zeilen. Sie können dann unsetdas 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)

verwandte Informationen