Мне нужно удалить элемент из массива в оболочке bash. Обычно я просто делал:
array=("${(@)array:#<element to remove>}")
К сожалению, элемент, который я хочу удалить, является переменной, поэтому я не могу использовать предыдущую команду. Вот пример:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Любая идея?
zsh
.array=( ${array[@]/$delete} )
работает должным образом в Bash. Вы просто пропустили=
?Ответы:
Следующее работает так, как вы хотите,
bash
иzsh
:Если нужно удалить более одного элемента:
Предостережение
Этот метод фактически удаляет префиксы, совпадающие
$delete
с элементами, а не обязательно целые элементы.Обновить
Чтобы действительно удалить точный элемент, вам нужно пройтись по массиву, сравнить цель с каждым элементом и использовать
unset
для удаления точного совпадения.Обратите внимание: если вы сделаете это, и один или несколько элементов будут удалены, индексы больше не будут непрерывной последовательностью целых чисел.
Дело в том, что массивы не были предназначены для использования в качестве изменяемых структур данных. Они в основном используются для хранения списков элементов в одной переменной без необходимости тратить символ в качестве разделителя (например, для хранения списка строк, которые могут содержать пробелы).
Если пробелы являются проблемой, вам необходимо перестроить массив, чтобы заполнить пробелы:
источник
$ array=(sun sunflower)
$ delete=(sun)
$ echo ${array[@]/$delete}
результатыflower
(pluto1 pluto2 pippo)
вы получите(1 2 pippo)
.for element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done
Вы можете создать новый массив без нежелательного элемента, а затем вернуть его старому массиву. Это работает в
bash
:Это дает:
источник
Это самый прямой способ сбросить значение, если вы знаете его положение.
источник
echo ${array[1]}
, вы получите нулевую строку. А чтобы получитьthree
нужно сделатьecho ${array[2]}
. Так чтоunset
это неправильный механизм для удаления элемента в массиве bash.${array[1]+x}
это пустая строка, поэтомуarray[1]
не установлено.unset
не меняет индексы остальных элементов. Аргумент для неустановленного значения указывать не нужно. Способ уничтожения элемента массива описан в руководстве Bash .${array[1]}
существует только потому, что его размер равен 2. Если вам нужны индексы, проверьте${!array[@]}
.Вот однострочное решение с mapfile:
Пример:
Этот метод обеспечивает большую гибкость за счет изменения / обмена командой grep и не оставляет пустых строк в массиве.
источник
printf '%s\n' "${array[@]}"
вместо этого уродливойIFS
/echo
вещи.-d $'\0'
отлично работает, а просто-d
без аргумента - нет.-d $'\0'
то же самое-d $'\0 something'
или просто-d ''
.$'\0'
для ясностиЭтот ответ относится к случаю удаления нескольких значений из больших массивов, когда важна производительность.
Самые популярные решения: (1) замена шаблона в массиве или (2) итерация по элементам массива. Первый быстрый, но может работать только с элементами, имеющими отдельный префикс, второй - O (n * k), n = размер массива, k = удаляемые элементы. Ассоциативный массив - это относительно новая функция, которая, возможно, не использовалась при первоначальной публикации вопроса.
Для случая точного совпадения с большими n и k возможно улучшить производительность с O (n k) до O (n + k log (k)). На практике O (n) при условии, что k намного меньше n. Большая часть ускорения основана на использовании ассоциативного массива для идентификации элементов, которые необходимо удалить.
Производительность (размер n-массива, k-значения для удаления). Измерение производительности в секундах пользовательского времени
Как и ожидалось,
current
решение линейно по отношению к N * K, аfast
решение практически линейно по отношению к K с гораздо более низкой константой.fast
Раствор немного медленнее по сравнению сcurrent
решением , когда к = 1, из - за дополнительной настройки.«Быстрое» решение: массив = список ввода, delete = список значений для удаления.
Сравнение с
current
решением на основе ответа, получившего наибольшее количество голосов.источник
Вот небольшая (вероятно, очень специфичная для bash) функция, включающая косвенное обращение к переменной bash и
unset
; это общее решение, которое не включает в себя замену текста или отбрасывание пустых элементов и не имеет проблем с цитированием / пробелами и т. д.Используйте его как
delete_ary_elmt ELEMENT ARRAYNAME
без$
сигилы. Включите== $word
for== $word*
для совпадений префиксов; использовать${elmt,,} == ${word,,}
для совпадений без учета регистра; и т.д., все, что[[
поддерживает bash .Он работает, определяя индексы входного массива и повторяя их в обратном порядке (поэтому удаление элементов не нарушает порядок итераций). Чтобы получить индексы, вам нужно получить доступ к входному массиву по имени, что можно сделать с помощью косвенного обращения к переменной bash
x=1; varname=x; echo ${!varname} # prints "1"
.Вы не можете получить доступ к массивам по имени, например
aryname=a; echo "${$aryname[@]}
, это дает вам ошибку. Вы не можете этого сделатьaryname=a; echo "${!aryname[@]}"
, это дает вам индексы переменнойaryname
(хотя это не массив). Что ДЕЙСТВИТЕЛЬНО работает, так это тоaryref="a[@]"; echo "${!aryref}"
, что будет печатать элементы массиваa
, сохраняя цитирование слов оболочки и пробелы точно так же, какecho "${a[@]}"
. Но это работает только для печати элементов массива, а не для печати его длиной или индексов (aryref="!a[@]"
илиaryref="#a[@]"
или"${!!aryref}"
или"${#!aryref}"
, все они терпят неудачу).Поэтому я копирую исходный массив по его имени через косвенную адресацию bash и получаю индексы из копии. Чтобы перебирать индексы в обратном порядке, я использую цикл for в стиле C. Я также мог бы сделать это, обратившись к индексам через
${!arycopy[@]}
и изменив их с помощьюtac
, котораяcat
меняет порядок строк ввода.Функциональное решение без косвенного
eval
обращения к переменным, вероятно, должно быть задействовано , что может быть безопасным или небезопасным для использования в этой ситуации (я не могу сказать).источник
delete_ary_elmt "d" array
а затем повторно распечатать массив. Вы увидите, что удален не тот элемент. Удаление последнего элемента также никогда не сработает.Чтобы расширить приведенные выше ответы, для удаления нескольких элементов из массива без частичного сопоставления можно использовать следующее:
В результате получится массив, содержащий: (два на два три три четыре "один шесть")
источник
Если кто-то окажется в положении, когда ему нужно запомнить значения set -e или set -x и иметь возможность их восстановить, ознакомьтесь с этой сутью, которая использует первое решение для удаления массива для управления своим собственным стеком:
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
источник
Только частичный ответ
Чтобы удалить первый элемент в массиве
Чтобы удалить последний элемент в массиве
источник
unset
.array0
в текущем каталоге, то, посколькуarray[0]
это glob, он сначала будет расширенarray0
до команды unset.С помощью
unset
Чтобы удалить элемент по определенному индексу, мы можем использовать,
unset
а затем скопировать в другой массив. Только простоunset
в этом случае не требуется. Посколькуunset
не удаляет элемент, он просто устанавливает нулевую строку для определенного индекса в массиве.Выход
С помощью
:<idx>
Мы можем удалить некоторый набор элементов, используя
:<idx>
также. Например, если мы хотим удалить 1-й элемент, мы можем использовать,:1
как указано ниже.Выход
источник
Сценарий оболочки POSIX не имеет массивов.
Так что, скорее всего, вы используете определенный диалект, например
bash
, korn shells илиzsh
.Поэтому на ваш вопрос пока нет ответа.
Может быть, это сработает для вас:
источник
На самом деле, я только что заметил, что синтаксис оболочки в некоторой степени имеет встроенное поведение, которое позволяет легко реконструировать массив, когда, как указано в вопросе, элемент должен быть удален.
Обратите внимание, как мы построили массив, используя
x+=()
синтаксис bash ?Фактически вы могли бы добавить с ним более одного элемента, содержимое целого другого массива одновременно.
источник
http://wiki.bash-hackers.org/syntax/pe#substring_removal
Чтобы выполнить полное удаление элемента, вы должны выполнить команду unset с оператором if. Если вы не заботитесь об удалении префиксов из других переменных или о поддержке пробелов в массиве, вы можете просто отбросить кавычки и забыть о циклах for.
См. В примере ниже несколько различных способов очистки массива.
Вывод
Надеюсь, это поможет.
источник
В ZSH это очень просто (обратите внимание, что здесь используется более совместимый с bash синтаксис, чем необходимо, где это возможно, для простоты понимания):
Полученные результаты:
источник
Также существует этот синтаксис, например, если вы хотите удалить второй элемент:
что на самом деле представляет собой объединение двух вкладок. Первый от индекса 0 до индекса 1 (исключительный) и второй от индекса 2 до конца.
источник
Что я делаю:
БАМ, этот предмет удален.
источник
array=('first item' 'second item')
.Это быстрое и грязное решение, которое будет работать в простых случаях, но сломается, если (а)
$delete
в каких-либо элементах есть специальные символы регулярных выражений или (б) в любых элементах вообще есть пробелы. Начиная с:Удалите все записи, точно соответствующие
$delete
:в результате
echo $array
-> pippo, и убедившись, что это массив:echo $array[1]
-> pippofmt
немного неясен:fmt -1
переносит на первый столбец (чтобы поместить каждый элемент в отдельную строку. Вот где возникает проблема с элементами вfmt -999999
пробелах ). разворачивает его обратно на одну строку, возвращая пробелы между элементами. Есть и другие способы сделать это, напримерxargs
.Приложение: если вы хотите удалить только первое совпадение, используйте sed, как описано здесь :
источник
Как насчет чего-то вроде:
источник
Для того, чтобы избежать конфликтов с индексом массива с помощью
unset
- см https://stackoverflow.com/a/49626928/3223785 и https://stackoverflow.com/a/47798640/3223785 для получения дополнительной информации - переназначить массив себе:ARRAY_VAR=(${ARRAY_VAR[@]})
.[Ссылка: https://tecadmin.net/working-with-array-bash-script/ ]
источник
источник