Reputation: 954
I will be short, what i have is
array=( one.a two.b tree.c four.b five_b_abc)
I want this
array=( two.b four.b five_b_abc )
from here i found this
# replace any array item matching "b*" with "foo"
array=( foo bar baz )
array=( "${array[@]/%b*/foo}" )
echo "${orig[@]}"$'\n'"${array[@]}"
how ever this does not work
array2=( ${array[@]//%^.p/})
result array2=array
this deletes all with p
array2=(${array[@]/*p*/})
result array2=( one.a tree.c )
I need an idea how to do add ^p (all accept p), and get my solution
array2=(${array[@]/*^p*/}
Its a rather large array about 10k elements where i need to do this i need it to be as fast as possible with data so please no loop solutions.
Upvotes: 4
Views: 3737
Reputation: 21
If you set IFS to $'\n', then you could use the echo command on the array with an '*' as the subscript, instead of printf with an '@':
IFS=$'\n'; a=($(echo "${a[*]}" | sed '/.*[5-9]...$/!d'))
The '*' subscript concatenates the array elements together, separated by the IFS. (I have only recently learned this myself, by the way.)
Upvotes: 0
Reputation: 6856
Edit: added timing comparisons (at end) and got rid of the tr
It is possible to replace the content of array elements using bash Parameter expansion, ie. ${var[@]....}
but you cannot actually delete the elements. The best you can get with parameter expansion is to make null ("") those elements you do not want.
Instead, you can use printf
and sed
, and IFS
. This has the advantabe of allowing you to use full regular expression syntax (not just the shell globbing expressions)... and it is much faster than using a loop...
This example leaves array elements which contain c
Note: This method caters for spaces in data. This is achieved via IFS=\n
IFS=$'\n'; a=($(printf '%s\n' "${a[@]}" |sed '/c/!d'))
Here it is again with before/after dumps:
#!/bin/bash
a=(one.ac two.b tree.c four.b "five b abcdefg" )
echo "======== Original array ===="
printf '%s\n' "${a[@]}"
echo "======== Array containing only the matched elements 'c' ===="
IFS=$'\n'; a=($(printf '%s\n' "${a[@]}" |sed '/c/!d'))
printf '%s\n' "${a[@]}"
echo "========"
Output:
======== Original array ====
one.ac
two.b
tree.c
four.b
five b abcdefg
======== Array containing only the matched elements 'c' ====
one.ac
tree.c
five b abcdefg
========
For general reference: Testing an array containing 10k elements. selecting 5k:
a=( a\ \ \ \ b{0..9999} )
The printf method took: 0m0.226s
(results in sequential index values)
The first loop method: 0m4.007s
(it leaves gaps in index values)
The second loop method: 0m7.862s
(results in sequential index values)
printf method:
IFS=$'\n'; a=($(printf '%s\n' "${a[@]}" |sed '/.*[5-9]...$/!d'))
1st loop method:
iz=${#a[@]}; j=0
for ((i=0; i<iz; i++)) ;do
[[ ! "${a[i]}" =~ .*[5-9]...$ ]] && unset a[$i]
done
2nd loop method:
iz=${#a[@]}; j=0
for ((i=0; i<iz; i++)) ;do
if [[ ! "${a[i]}" =~ .*[5-9]...$ ]] ;then
unset a[$i]
else
a[$j]="${a[i]}=$i=$j"; ((j!=i)) && unset a[$i]; ((j+=1));
fi
done
Upvotes: 6
Reputation: 161674
You can try this:
array2=(`echo ${array[@]} | sed 's/ /\n/g' | grep b`)
Upvotes: 5