Как правильно собрать массив строк в zsh

В Gnome 3:

gsettings set org.gnome.settings-daemon.peripherals.touchpad tap-to-click true
42
13.04.2017, 15:36
3 ответа

TL, DR:

array_of_lines=("${(@f)$(my_command)}")

Первая ошибка (→ Q2): IFS='\n' наборы IFS к этим двум символам \ и n. Установить IFS к новой строке использовать IFS=$'\n'.

Вторая ошибка: для установки переменной на значение массива Вам нужны круглые скобки вокруг элементов: array_of_lines=(foo bar).

Это работало бы, за исключением того, что это разделяет пустые строки, потому что последовательный пробел рассчитывает как единственный разделитель:

IFS=$'\n' array_of_lines=($(my_command))

Можно сохранить пустые строки кроме в самом конце путем удвоения пробельного символа в IFS:

IFS=$'\n\n' array_of_lines=($(my_command))

Чтобы продолжать запаздывать пустые строки также, необходимо было бы добавить что-то к выводу команды, потому что это происходит в самой замене команды, не от парсинга его.

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

(принятие вывода my_command не заканчивается в неразграниченной строке; также обратите внимание потерю статуса выхода my_command)

Обратите внимание что все отрывки выше отпуска IFS с его значением не по умолчанию, таким образом, они могут испортить последующий код. Сохранять установку IFS локальный, помещает все это в функцию, где Вы объявляете IFS локальный (здесь также заботящийся о сохранении статуса выхода команды):

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  ret=$?
  unset 'array_of_lines[-1]'
  return $ret
}
collect_lines my_command

Но я рекомендую не смешать с IFS; вместо этого, используйте f флаг расширения для разделения на новых строках (→ Q1):

array_of_lines=("${(@f)$(my_command)}")

Или сохранить запаздывание пустых строк:

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

Значение IFS не имеет значения там. Я подозреваю, что Вы использовали команду, которая разделяет на IFS распечатать $array_of_lines в Ваших тестах (→ Q3).

71
27.01.2020, 19:35
  • 1
    Это так сложно! "${(@f)...}" совпадает с ${(f)"..."}, но по-другому. (@) в средствах двойных кавычек “приводят к одному слову на элемент массива” и (f) означает “разделение в массив новой строкой”. PS: свяжите с документами –  flying sheep 17.08.2015, 14:11

Две проблемы: во-первых, по-видимому двойные кавычки также не интерпретируют Escape обратной косой черты (извините об этом :). Использовать $'...' кавычки. И согласно man zshparam, для сбора слов в массиве, необходимо включить их в круглую скобку. Таким образом, это работает:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

Я не могу ответить на Вашего Q3. Я надеюсь, что никогда не должен буду знать такие тайные вещи :).

4
27.01.2020, 19:35

Вы также можете использовать tr для замены новой строки пробелом:

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  break
done
-2
27.01.2020, 19:35

Теги

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