5

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.

0

3 Answers 3

7

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
Sign up to request clarification or add additional context in comments.

1 Comment

I wish to give my thanks to you,too. site ypu added is in my bookmarks from now on.
5

You can try this:

array2=(`echo ${array[@]} | sed 's/ /\n/g' | grep b`)

1 Comment

both solutions are great, but i have used yours
0

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.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.