Gal Fleissig
Gal Fleissig

Reputation: 33

Bash function with array won't work

I am trying to write a function in bash but it won't work. The function is as follows, it gets a file in the format of:

1 2 first 3
4 5 second 6
...

I'm trying to access only the strings in the 3rd word in every line and to fill the array "arr" with them, without repeating identical strings. When I activated the "echo" command right after the for loop, it printed only the first string in every iteration (in the above case "first").

Thank you!

function storeDevNames {

n=0
b=0
while read line; do
    line=$line
    tempArr=( $line )
    name=${tempArr[2]}
    for i in $arr ; do
        #echo ${arr[i]}
        if [ "${arr[i]}" == "$name" ]; then
            b=1
            break
        fi
    done
    if [ "$b" -eq 0 ]; then
        arr[n]=$name
        n=$(($n+1))
    fi
    b=0
done < $1
}

Upvotes: 0

Views: 94

Answers (3)

choroba
choroba

Reputation: 241888

The following line seems suspicious

    for i in $arr ; do

I changed it as follows and it works for me:

#! /bin/bash

function storeDevNames {
    n=0
    b=0
    while read line; do
        # line=$line # ?!
        tempArr=( $line )
        name=${tempArr[2]}
        for i in "${arr[@]}" ; do
            if [ "$i" == "$name" ]; then
                b=1
                break
            fi
        done
        if [ "$b" -eq 0 ]; then
            arr[n]=$name
            (( n++ ))
        fi
        b=0
    done
}

storeDevNames < <(cat <<EOF 
1 2 first 3
4 5 second 6
7 8 first 9
10 11 third 12
13 14 second 15
EOF
)

echo "${arr[@]}"

Upvotes: 1

4ae1e1
4ae1e1

Reputation: 7634

You're using the wrong tool. awk is designed for this kind of job.

awk '{ if (!seen[$3]++) print $3 }' <"$1"

This one-liner prints the third column of each line, removing duplicates along the way while preserving the order of lines (only the first occurrence of each unique string is printed). sort | uniq, on the other hand, breaks the original order of lines. This one-liner is also faster than using sort | uniq (for large files, which doesn't seem to be applicable in OP's case), since this one-liner linearly scans the file once, while sort is obviously much more expensive.

As an example, for an input file with contents

1 2 first 3
4 5 second 6
7 8 third 9
10 11 second 12
13 14 fourth 15

the above awk one-liner gives you

first
second
third
fourth

To put the results in an array:

arr=( $(awk '{ if (!seen[$3]++) print $3 }' <"$1") )

Then echo ${arr[@]} will give you first second third fourth.

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84561

You can replace all of your read block with:

arr=( $(awk '{print $3}' <"$1" | sort | uniq) )

This will fill arr with only unique names from the 3rd word such as first, second, ... This will reduce the entire function to:

function storeDevNames {
    arr=( $(awk '{print $3}' <"$1" | sort | uniq) )
}

Note: this will provide a list of all unique device names in sorted order. Removing duplicates also destroys the original order. If preserving the order accept where duplicates are removed, see 4ae1e1's alternative.

Upvotes: 1

Related Questions