I need to remove an element from an array in bash shell. Generally I'd simply do:
array=("${(@)array:#<element to remove>}")
Unfortunately the element I want to remove is a variable so I can't use the previous command. Down here an example:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Any idea?
This answer is specific to the case of deleting multiple values from large arrays, where performance is important.
The most voted solutions are (1) pattern substitution on an array, or (2) iterating over the array elements. The first is fast, but can only deal with elements that have distinct prefix, the second has O(n*k), n=array size, k=elements to remove. Associative array are relative new feature, and might not have been common when the question was originally posted.
For the exact match case, with large n and k, possible to improve performance from O(nk) to O(n+klog(k)). In practice, O(n) assuming k much lower than n. Most of the speed up is based on using associative array to identify items to be removed.
Performance (n-array size, k-values to delete). Performance measure seconds of user time
As expected, the
current
solution is linear to N*K, and thefast
solution is practically linear to K, with much lower constant. Thefast
solution is slightly slower vs thecurrent
solution when k=1, due to additional setup.The 'Fast' solution: array=list of input, delete=list of values to remove.
Benchmarked against
current
solution, from the most-voted answer.This is a quick-and-dirty solution that will work in simple cases but will break if (a) there are regex special characters in
$delete
, or (b) there are any spaces at all in any items. Starting with:Delete all entries exactly matching
$delete
:resulting in
echo $array
-> pippo, and making sure it's an array:echo $array[1]
-> pippofmt
is a little obscure:fmt -1
wraps at the first column (to put each item on its own line. That's where the problem arises with items in spaces.)fmt -999999
unwraps it back to one line, putting back the spaces between items. There are other ways to do that, such asxargs
.Addendum: If you want to delete just the first match, use sed, as described here:
There is also this syntax, e.g. if you want to delete the 2nd element :
which is in fact the concatenation of 2 tabs. The first from the index 0 to the index 1 (exclusive) and the 2nd from the index 2 to the end.
To avoid conflicts with array index using
unset
- see https://stackoverflow.com/a/49626928/3223785 and https://stackoverflow.com/a/47798640/3223785 for more information - reassign the array to itself:ARRAY_VAR=(${ARRAY_VAR[@]})
.[Ref.: https://tecadmin.net/working-with-array-bash-script/ ]
If anyone finds themselves in a position where they need to remember set -e or set -x values and be able to restore them, please check out this gist which uses the first array deletion solution to manage it's own stack:
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
How about something like: