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 $list
tambié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 $list
es 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 wmctl
elementos de salida, no necesita una matriz y su uso $list
está perfectamente bien.
Si seerauna matriz, $list
solo 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 while
bucle suele ser mejor que un for
bucle 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 awk
comando 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 echo
s una vez que esté satisfecho de que está haciendo lo correcto.
Como se señaló en los comentarios, xargs
es 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 time
comando 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
list
es 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
-i
y-a
se puede combinar en-ia
.$i
no es descriptivo y lo usaría$Window
en 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á for
bucles de líneas, luego puede realizar unset
el 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)