Martin
Martin

Reputation: 87

Loop through dynamically generated array in bash

I'm trying to write a script that performs actions of files with different extensions. To make it as easy to add different actions as possible, the extensions are read from an array, files are found via the "find" command, and results returned to a dynamically generated array named after the file extension that was searched.

To add a new extension to search for I can simply add to the file_ext array.

I create the array like this:

file_exts=("dsd" "dsdd" "dmesg")

for ext in "${file_exts[@]}"
do

        echo "Finding $ext files"
        eval var="exts_$ext"

        declare -a $var="($( find "$dirs_target" -name "*.$ext"))"

done

The arrays are created correctly, and I can manually echo "${exts_dsd[0]} ${exts_dsd[1]}" and see the entries, However, I can't find a way of looping through each entry in the dynamically assigned arrays.

I have tried a few combinations using eval, and I can print out the first entry in the array, IE just referencing "$exts_dsd" Here are two things I've already tried:

for varname in "${!exts_@}"
do

        for entry in ${varname[@]}
        do
                echo "$varname : $entry"
        done

        eval value=\$${varname[@]}       

        echo "$varname=$value"
done

How can I loop through each entry in the above for loop, so I can print out all the entries in all the dynamically created arrays?

Here is a complete test script:

#! /bin/bash


file_exts=("dsd" "dsdd" "dmesg")
dirs_target="/tmp/arraytest/"

echo "Creating $dirs_target"
if [[ ! -d "$dirs_target" ]]; then

        if ! mkdir "$dirs_target"; then
                echo "Couldn't create temp dir"
                exit 1
        fi
fi

echo "Creating test files"
for tmpfile in $( seq 0 5 )
do
        echo -e "\tCreating $dirs_target$tmpfile.dsd"
        if ! touch "$dirs_target/$tmpfile.dsd"; then
                echo "Coudn't create $dirs_target/test$tmpfile.dsd"
                exit 1
        fi
done

echo ""
echo "-----Finding Files-----"
for ext in "${file_exts[@]}"
do

        echo "Finding $ext files"
        eval var="exts_$ext"

        declare -a $var="($( find "$dirs_target" -name "*.$ext"))"

done
echo ""
echo "-----File Extensions-----"
for varname in "${!exts_@}"
do

        for entry in ${varname[@]}
        do
                echo "$varname : $entry"
        done
        eval value=\$${varname[@]}
        #echo "$varname=$value"
done

echo ""
echo "Finishing."
rm -rf "$dirs_target"

Upvotes: 0

Views: 227

Answers (2)

Martin
Martin

Reputation: 87

I've found the answer. I had the eval statement slightly wrong.

echo "-----File Extensions-----"
for varname in "${!exts_@}"
do

        echo "varname=$varname"

        eval testvalue="\${$varname[@]}"

        for entry in $testvalue
        do
                echo -e "\tFile: $entry"
        done

done

As a bonus, I've also figured out how to add to a dynamically created array

 var="table_$entry"
 declare -a $var   

 while read -r line
 do
        eval $var+=\(\'"$line"\'\)

 done < "$dirs_table"

Upvotes: 0

choroba
choroba

Reputation: 241768

To loop over the entries, you have to use the same trick as when creating them: just store the variable name in a variable. The point is to include the [@] index, too, which will be correctly recognized in the indirection:

for varname in "${!exts_@}" ; do
    arr=$varname'[@]'
    for entry in "${!arr}" ; do
        echo "$varname : $entry"
    done
done

Also note that eval isn't needed in

# eval var="exts_$ext"
var=exts_$ext  # works even better!

Upvotes: 2

Related Questions