Фильтрация массива довольно сложна, если учесть возможность элементов, содержащих пробелы (не говоря уже о «странных» символах).В частности, ответы, данные до сих пор (относящиеся к различным формам ${x[@]//pref*/}
), потерпят неудачу с такими массивами.
Я несколько исследовал эту проблему и нашел решение, однако это не является хорошим однострочником.Но, по крайней мере, так.
Для иллюстративных примеров предположим, что arr
называет массив, который мы хотим отфильтровать.Начнем с основного выражения:
for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done
ARR=("${ARR[@]}")
Уже есть несколько элементов, о которых стоит упомянуть:
"${!ARR[@]}"
вычисляет индексы массива (в отличие от элементов). - Форма
"${!ARR[@]}"
обязательна.Вы не должны пропускать кавычки или изменять @
на *
.В противном случае выражение будет разбиваться на ассоциативных массивах, где ключи содержат пробелы (например). - Часть после
do
может быть любой, какой вы захотите.Идея состоит лишь в том, что вы должны сделать unset
, как показано для элементов, которые вы не хотите иметь в массиве. - Рекомендуется или даже необходимо , чтобы использовать
-v
и кавычки с unset
, иначе могут произойти плохие вещи. - Если часть после
do
соответствует предложенной выше, вы можете использовать &&
или ||
, чтобы отфильтровать элементы, которые либоПройдите или не выполните условие. - Вторая строка, переназначение
ARR
, необходима только для неассоциативных массивов, а прервется с ассоциативными массивами .(Я не быстро придумал универсальное выражение, которое будет обрабатывать оба, пока мне не нужно…).Для обычных массивов это необходимо, если вы хотите иметь последовательные индексы.Поскольку unset
в элементе массива не изменяет (опускает на единицу) элементы более высоких индексов - он просто делает дыру в индексах.Теперь, если вы только перебираете массив (или расширяете его целиком), это не проблема.Но для других случаев вам нужно переназначить индексы.Также обратите внимание, что если у вас есть дыра в индексах, прежде чем она будет также удалена.Поэтому, если вам необходимо сохранить существующие дыры, необходимо выполнить больше логики, кроме unset
и окончательного переназначения.
Теперь, когда дело доходит до условия.Выражение [[ ]]
- это простой способ, если вы можете его использовать.(См. здесь .) В частности, он поддерживает сопоставление регулярных выражений с использованием Расширенные регулярные выражения .(См. здесь .) Также будьте осторожны с использованием grep
или любого другого линейного инструмента для этого, если вы ожидаете, что элементы массива могут содержать не только пробелы, но и новые строки.(Хотя я думаю, что очень неприятное имя файла может иметь символ новой строки…)
Если обратиться к самому вопросу, выражение [[ ]]
должно быть:
[[ ${ARR[$index]} =~ ^pref ]]
(с && unset
как указано выше)
Давайте, наконец, посмотрим, как это работает с этими трудными случаями.Сначала мы создаем массив:
declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces")'
ARR+=($'pref\nwith\nnew line')
ARR+=($'\npref with new line before')
. Мы можем увидеть, что у нас есть все сложные случаи, запустив declare -p ARR
и получив:
declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces" [7]="pref
with
new line" [8]="
pref with new line before")'
Теперь запустим выражение фильтра:
for index in "${!ARR[@]}" ; do [[ ${ARR[$index]} =~ ^pref ]] && unset -v 'ARR[$index]' ; done
и другой тест (declare -p ARR
) дает ожидаемый результат:
declare -a ARR='([1]="bar" [2]="foo" [4]="baz" [8]="
pref with new line before")'
обратите внимание, как были удалены все элементы, начиная с pref
, но индексы не изменились.Также обратите внимание, что ${ARRAY[8]}
все еще там, поскольку он начинается с новой строки, а не pref
.
Теперь для окончательного переназначения:
ARR=("${ARR[@]}")
и проверки (declare -p ARR
):
declare -a ARR='([0]="bar" [1]="foo" [2]="baz" [3]="
pref with new line before")'
, что именно то, что ожидалось.
Для заключительных нот.Было бы хорошо, если бы это могло быть изменено на гибкую однострочную.Но я не думаю, что есть способ сделать его короче и проще, как сейчас, без определения функций или тому подобного.
Что касается функции, то было бы неплохо, чтобы она принимала массив, возвращала массиви легко настроить тест, чтобы исключить или сохранить.Но я не достаточно хорош с Башом, чтобы сделать это сейчас.