Для цикла с помощью массивов лучше, чем использование полевого разделения на простой переменной?

У меня есть несколько открытых приложений. Выполнение wmctrl и передача по каналу вывода к awk перечисляют окно ID (исключая "липкие" окна) как это:

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

Я могу отправить этот вывод в wmctrl для закрытия всех этих окон:

  • окна без содержания, которое должно быть сохранено и окна, которые не нуждаются в ответе, закроются, не спрашивая меня, но

  • окна, такие как те из редакторов с несохраненным содержанием или терминалами, выполняющими процесс, закроются "корректно": соответствующее приложение представит окно, разрешающее мне сохранить изменения или отменить изменения или сообщить мне о процессе, который это все еще выполняет.

Следующий сценарий, присвоенный подходящему ярлыку, работам:

#!/bin/bash

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

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

Я нашел что более простое (мне) for i in $list также работы.

Там какая-либо причина состоит в том, чтобы предпочесть один по другому?


"липкий" и "корректно" условия от man wmctrl.

11
задан 17 October 2019 в 18:49

4 ответа

В Вашем сценарии $list совпадает с ${list[@]}.

Последний является синтаксисом массива, но в Вашем сценарии это - нормальная переменная.


Поскольку у Вас нет пробела в Вашем wmctl выходные параметры, Вам не нужны массив и использование $list прекрасно подходит.


Если это был массив, $list только был бы первый объект массива (=> item1) и ${list[@]} расширился бы на все объекты (=> item1 item2 item3).

Но то, что Вы действительно хотели, если это на самом деле был массив, "${list[@]}" (с кавычками), который расширяется на "item1" "item2" "item3", таким образом, это не дросселировало бы на пробеле.


(Читать)

13
ответ дан 23 November 2019 в 03:52

A while цикл часто является лучшим соответствием, чем a for цикл для обработки вывода команды, разрешения Вас к производственным линейкам непосредственно вместо того, чтобы хранить их в списке или массиве.

В этом случае это позволяет Вам избегать awk команда в целом:

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

Удалите echos, после того как Вы рады, что это делает правильную вещь.

Как отмечено в комментариях, xargs другая опция - но это становится хитрым, когда Вы хотите сделать больше чем одну вещь с каждым arg.

8
ответ дан 23 November 2019 в 03:52

Ответьте на оригинальное название

Оригинальное название спросило, "какой для цикла лучше".

Для меня лучший метод является самым быстрым. Для обнаружения предварительно ожидают time управляйте к своему сценарию или функции. Некоторые примеры:

$ time du -s

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

$ time ls

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

Важно сбросить кэшируемые буферные тесты промежутка хотя:

Если два цикла будут о том же в скорости, то я выберу тот с лучшей удобочитаемостью.

Объем этого вопроса, делает скорость не важной, хотя, потому что большая часть времени проведена, ожидая ввода данных пользователем и существует только максимум 10 окон, открытых для большинства людей.


Ответьте на тело вопроса

Другие ответы фокусируются на перезаписи сценария, таким образом, я дам свою ценность за два цента также.

Строка:

list=$(wmctrl -l | awk ' !/-1/ { print $1 } ')
  • уродливо, если намерение состоит в том, чтобы быть массивом
  • list является универсальным и не описательным

Таким образом, я использовал бы:

Windows=( $(wmctrl -l | awk ' !/-1/ { print $1 } ') )
  • Внешний набор () говорит удару/оболочке, что все внутри - элемент массива, очерченный пробелами.
  • Windows - то, что мы говорим о том, таким образом, это - описательное имя массива.
  • Windows является множественным числом, таким образом, соглашение о присвоении имен помогает определить, что это - массив.

Строка:

wmctrl -i -a $i
  • -i и -a может быть объединен в -ia.
  • $i является неописательным, я использовал бы $Window вместо этого.

Существует два способа записать более короткому больше читаемого сценария, сначала с массивом:

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

второй без массива:

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

Я предпочитаю метод массива, потому что я пытаюсь узнать больше о них и хотеть использовать их как можно больше. Выбор является Вашим как бы то ни было.

5
ответ дан 23 November 2019 в 03:52

Можно справиться без массива. Установка IFS к новой строке позволит for к строкам цикла Вы можете затем unset IFS в цикле, не влияя на сам цикл.

#!/bin/bash

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

(сброс позиционных параметров является аккуратным приемом для разделения строки в к полям).

если необходимо использовать массив, можно использовать mapfile и использовать в своих интересах функцию обратного вызова для создания чего-то подобного циклу. Для маленького набора повторений это может быть преимущество для использования более простого вызова функции.

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

(долгая версия):

#!/bin/bash

f(){
    set -- $@
    if (($3 > -1)); then
        wmctrl -i -a $2 -c $2
    fi
}
mapfile -c 1 -C f -t < <(wmctrl -l)
5
ответ дан 23 November 2019 в 03:52

Другие вопросы по тегам:

Похожие вопросы: