Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bash array leave elements containing string

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.

like image 557
nkvnkv Avatar asked Dec 21 '22 00:12

nkvnkv


1 Answers

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
like image 149
Peter.O Avatar answered Jan 01 '23 14:01

Peter.O