Tenho vários aplicativos abertos. Correndowmctrle canalizando a saída paraestranholista os IDs das janelas (excluindo janelas "fixas") assim:
$ wmctrl -l | awk ' !/-1/ { print $1 } '
0x00a00018
0x04800005
0x04e00005
0x04400003
0x05000003
0x0540002b
0x05a00012
0x05800002
0x05c00003
$
Posso enviar esta saída parawmctrlpara fechar todas essas janelas:
janelas sem conteúdo que precisa ser salvo e janelas que não precisam de resposta serão fechadas sem me perguntar, mas
janelas como as de editores com conteúdo não salvo ou terminais executando um processo serão fechadas "normalmente": a respectiva aplicação apresentará uma janela que me permite salvar ou descartar alterações ou me informar sobre um processo que ainda está em execução.
O seguinte script, atribuído a um atalho adequado, funciona:
#!/bin/bash
list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
for i in ${list[@]}
do
wmctrl -i -a $i
wmctrl -i -c $i
done
Descobri que o mais simples (para mim) for i in $list
também funciona.
Existe alguma razão para preferir um ao outro?
"pegajoso" e "graciosamente" são termos de man wmctrl
.
Responder1
No seu script $list
é o mesmo que ${list[@]}
.
A última é uma sintaxe de array, mas no seu script é uma variável normal.
Como você não tem espaços em branco nos wmctl
itens de saída, não precisa de um array e usar $list
é perfeitamente adequado.
Se issoeraum array, $list
seria apenas o primeiro item do array (=> item1
) e
${list[@]}
se estenderia a todos os itens (=> item1 item2 item3
).
Mas o que você realmente queria se realmenteerauma matriz é "${list[@]}"
(com aspas) que se estende até "item1" "item2" "item3"
, portanto, não engasgaria com espaços em branco.
(Ler)
Responder2
Um while
loop geralmente é mais adequado do que um for
loop para processar a saída do comando, permitindo processar linhas diretamente em vez de armazená-las em uma listaouvariedade.
Neste caso, permite evitar awk
completamente o comando:
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
Remova o echo
s quando estiver satisfeito por estar fazendo a coisa certa.
Conforme observado nos comentários, xargs
é outra opção - mas fica complicado quando você deseja fazer mais de uma coisa com cada arquivo arg
.
Responder3
Resposta ao título original
O título original perguntava "que tipo de loop for é melhor".
Para mim, o melhor método é o mais rápido. Para descobrir, acrescente o time
comando ao seu script ou função. Alguns exemplos:
$ time du -s
real 0m0.002s
user 0m0.003s
sys 0m0.000s
$ time ls
real 0m0.004s
user 0m0.000s
sys 0m0.004s
É importante liberar buffers em cache entre os testes:
Se dois loops tiverem aproximadamente a mesma velocidade, escolherei aquele com melhor legibilidade.
O escopo desta questão torna a velocidade irrelevante porque a maior parte do tempo é gasta aguardando a entrada do usuário e há apenas um máximo de 10 janelas abertas para a maioria das pessoas.
Resposta ao corpo da pergunta
Outras respostas se concentram em reescrever o roteiro, então também darei meus dois centavos.
A linha:
list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
- está malformado se a intenção for ser uma matriz
list
é genérico e não descritivo
Então eu usaria:
Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
- O conjunto externo de () informa ao bash/shell que tudo dentro é um elemento de array delineado por espaços.
- Estamos falando do Windows, portanto é um nome de array descritivo.
- Windows é plural, portanto a convenção de nomenclatura ajuda a identificar que é uma matriz.
A linha:
wmctrl -i -a $i
-i
e-a
pode ser combinado em-ia
.$i
não é descritivo, eu usaria$Window
.
Existem duas maneiras de escrever um script mais curto e mais legível, primeiro com um array:
#!/bin/bash
Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ' ) )
for Window in "${Windows[@]}" ; do wmctrl -ia $Window -c $Window ; done
segundo sem uma matriz:
#!/bin/bash
Windows=$(wmctrl -l | awk ' !/-1/ { print $1 } ' )
for Window in $Windows ; do wmctrl -ia $Window -c $Window ; done
Prefiro o método array porque estou tentando aprender mais sobre eles e quero usá-los o máximo possível. A escolha é sua, no entanto.
Responder4
Você pode gerenciar sem um array. ContextoSEpara nova linha permitirá for
fazer loop de linhas, você pode então usar unset
o IFS dentro do loop sem afetar o loop em si.
#!/bin/bash
IFS=$'\n'
for i in $(wmctrl -l); do
unset IFS
set -- $i
(($2 > -1)) && wmctrl -i -a $1 -c $1
done
(redefinindo oparâmetros posicionaisé um truque legal para dividir uma linha em campos).
se você precisar usar um array, você pode usararquivo de mapae aproveite a função de retorno de chamada para criar algo semelhante a um loop. Para um pequeno conjunto de iterações pode ser uma vantagem usar a chamada de função mais simples.
mapfile -c 1 -C 'f(){ set -- $@; (($3 >= 0)) && wmctrl -i -a $2 -c $2; }; f' -t < <(wmctrl -l)
(versão longa):
#!/bin/bash
f(){
set -- $@
if (($3 > -1)); then
wmctrl -i -a $2 -c $2
fi
}
mapfile -c 1 -C f -t < <(wmctrl -l)