¿Es mejor un bucle for que utiliza matrices que utilizar la división de campos en una variable simple?

¿Es mejor un bucle for que utiliza matrices que utilizar la división de campos en una variable simple?

Tengo varias aplicaciones abiertas. Correrwmctrly canalizar la salida aawkenumera los ID de las ventanas (excluyendo las ventanas "fijas") de esta manera:

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

Puedo enviar esta salida awmctrlpara cerrar todas estas ventanas:

  • Las ventanas sin contenido que deba guardarse y las ventanas que no necesitan respuesta se cerrarán sin preguntarme, pero

  • Las ventanas como las de los editores con contenido no guardado o terminales que ejecutan un proceso se cerrarán "con gracia": la aplicación respectiva presentará una ventana que me permitirá guardar cambios o descartarlos o informarme de un proceso que aún se está ejecutando.

El siguiente script, asignado a un acceso directo adecuado, funciona:

#!/bin/bash

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

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

Descubrí que lo más simple (para mí) for i in $listtambién funciona.

¿Hay alguna razón para preferir uno sobre el otro?


"pegajoso" y "con gracia" son términos de man wmctrl.

Respuesta1

En tu script $listes lo mismo que ${list[@]}.

Esta última es una sintaxis de matriz, pero en su secuencia de comandos es una variable normal.


Como no tiene espacios en blanco en sus wmctlelementos de salida, no necesita una matriz y su uso $listestá perfectamente bien.


Si seerauna matriz, $listsolo sería el primer elemento de la matriz (=> item1) y ${list[@]}se extendería a todos los elementos (=> item1 item2 item3).

Pero lo que realmente querías si realmenteerauna matriz "${list[@]}"(entre comillas) se extiende hasta "item1" "item2" "item3", por lo que no se ahogaría con los espacios en blanco.


(Leer)

Respuesta2

Un whilebucle suele ser mejor que un forbucle para procesar la salida del comando, lo que le permite procesar líneas directamente en lugar de almacenarlas en una lista.oformación.

En este caso, te permite evitar el awkcomando por completo:

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

Elimine la echos una vez que esté satisfecho de que está haciendo lo correcto.

Como se señaló en los comentarios, xargses otra opción, pero se vuelve complicada cuando quieres hacer más de una cosa con cada uno arg.

Respuesta3

Respuesta al título original.

El título original preguntaba "qué tipo de bucle for es mejor".

Para mí, el mejor método es el más rápido. Para averiguarlo, anteponga el timecomando a su script o función. Algunos ejemplos:

$ time du -s

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

$ time ls

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

Sin embargo, es importante vaciar los buffers en caché entre pruebas:

Si dos bucles tienen aproximadamente la misma velocidad, elegiré el que tenga mejor legibilidad.

Sin embargo, el alcance de esta pregunta hace que la velocidad sea irrelevante porque la mayor parte del tiempo se pasa esperando la entrada del usuario y solo hay un máximo de 10 ventanas abiertas para la mayoría de las personas.


Respuesta al cuerpo de la pregunta.

Otras respuestas se centran en reescribir el guión, así que también daré mi granito de arena.

La línea:

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
  • está mal formado si la intención es ser una matriz
  • listes genérico y no descriptivo

Entonces usaría:

Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
  • El conjunto externo de () le dice a bash/shell que todo lo que hay dentro es un elemento de matriz delimitado por espacios.
  • Estamos hablando de Windows, por lo que es un nombre de matriz descriptivo.
  • Windows es plural, por lo que la convención de nomenclatura ayuda a identificar que es una matriz.

La línea:

wmctrl -i -a $i
  • -iy -ase puede combinar en -ia.
  • $ino es descriptivo y lo usaría $Windowen su lugar.

Hay dos formas de escribir un script más corto y legible, primero con una matriz:

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

segundo sin una matriz:

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

Prefiero el método de matriz porque intento aprender más sobre ellos y quiero usarlos tanto como sea posible. Sin embargo, la elección es tuya.

Respuesta4

Puedes arreglártelas sin una matriz. ConfiguraciónIFSLa nueva línea permitirá forbucles de líneas, luego puede realizar unsetel IFS dentro del bucle sin afectar el bucle en sí.

#!/bin/bash

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

(restableciendo elparámetros posicionaleses un buen truco para dividir una línea en campos).

si necesita usar una matriz, puede usararchivo de mapay aproveche la función de devolución de llamada para crear algo similar a un bucle. Para un pequeño conjunto de iteraciones, puede ser una ventaja utilizar la llamada a función más simple.

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

(Versión larga):

#!/bin/bash

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

información relacionada