2

I'm having some Trouble with Bash's associative Arrays: I'm looking for a Solution to assign an Array with Filenames to the associative Array.

It should look like :

/Applications/Setup.app => ([0] = "/Applications/Setup.app/[email protected]"
                            [1] = "/Applications/Setup.app/[email protected]"
                            [2] = "/Applications/Setup.app/IDMusic [email protected]")

Declaring the Dictionary / Hash Table / Associative Array isn't the Problem :

declare -A Dictionary

To get the Keys I'm using "readarray", combined with "find" :

readarray -d '' Applications < <(find "/Applications" -name "*.app" -print0)

If a Directory has a Whitespace in it's Name it's not a Problem. Now I need to find the PNG-Files in Application's Directory :

if [ ${#Applications[*]} -gt 0 ]; then
    for App in "${Applications[@]}"; do
        readarray -d '' Files < <(find "$App" -iname "*.png" -type f -print0)

        for File in "${Files[@]}"; do
            Dictionary["$App"]+="$File"
        done
    done
fi

The Problem which I didn't understand is, how can I add the Result of second "find" to the declared associative Array as List of Files ? If I set the readarray with Delimiter ' ' some Files, containing Whitespaces, were broken.

The next Question is how can I get the Values in a for-Loop ? I tried this also with readarray, but this didn't work for Files with Whitespaces in it's Name.

for Key in "${!Dictionary[@]}"; do
    readarray -d ' ' Values <<< "${Dictionary[$Key]}"

    for Value in "${Values[@]}"; do
        echo "$Value"
    done
done

If I run the above Script this is what I get :

/Applications/Setup.app/[email protected]/Applications/Setup.app/[email protected]/Applications/Setup.app/IDMusic 
[email protected]

But I want this One :

/Applications/Setup.app/[email protected]
/Applications/Setup.app/[email protected]
/Applications/Setup.app/IDMusic [email protected]

Is there any Chance getting this Work with Bash 5.0.3 ?

The Goal is to use most of the Features without awk / grep / sed, so it runs with Bash's Built-In Commands.

1
  • 2
    The only idea I have is to store the lists in a format like JSON, using a tool like jq. But really, if you need 2-dimensional arrays, you shouldn't be using bash in the first place. Use a programming language with real data structures. Commented Jan 7, 2021 at 16:21

3 Answers 3

0

When populating the array with the filenames, consider using a character (as a delimiter) that does not exist in any of the filenames. [NOTE: as currently coded there is no delimiter hence the reason the filenames are jammed together as a single long line].

For example, assuming the filenames do not contain a linefeed (\n), and assuming the find returns the following:

/Applications/Setup.app/[email protected]
/Applications/Setup.app/[email protected]
/Applications/Setup.app/IDMusic [email protected]

We can populate the array like such:

unset      Dictionary
declare -A Dictionary

App="/Applications/Setup.app"

# for sake of example, simulate find results by processing a list of files

for file in "/Applications/Setup.app/[email protected]" "/Applications/Setup.app/[email protected]" "/Applications/Setup.app/IDMusic [email protected]"
do
    Dictionary[${App}]+=$'\n'"${file}"     # use '\n' as delimiter
done

while IFS=$'\n' read -r line
do
    [[ -n "${line}" ]] &&                  # skip the initial '\n'
    printf "|%s|\n" "${line}"
done <<< "${Dictionary[@]}"

NOTE: pipes added to printf format as visual delimiters

This generates:

|/Applications/Setup.app/[email protected]|
|/Applications/Setup.app/[email protected]|
|/Applications/Setup.app/IDMusic [email protected]|
Sign up to request clarification or add additional context in comments.

Comments

0

Aray of arrays, consider this approach:

[[ ${Applications[@]} ]] && {
    for i in "${!Applications[@]}"; do
        readarray -d '' "Files$i" < <(find "${Applications[$i]}" -iname "*.png" -type f -print0)
        Dictionary["${Applications[$i]}"]="Files$i"
    done
}

And then to retrive values use this:

for key in "${!Dictionary[@]}"; do
    val="${Dictionary[$key]}[@]"
    echo "$key: ${!val}"
done

And check here

Comments

0

By just modifying Ivan's nice answer, how about:

declare -A Dictionary
readarray -d '' Applications < <(find "/Applications" -name "*.app" -print0)

for i in "${!Applications[@]}"; do
    readarray -d '' "Files$i" < <(find "${Applications[$i]}" -iname "*.png" -type f -print0)
    Dictionary["${Applications[$i]}"]="Files$i"
done

for key in "${!Dictionary[@]}"; do
    echo "$key => "
    declare -n ary="${Dictionary[$key]}"
    for j in "${!ary[@]}"; do
        echo "  [$j] = ${ary[j]}"
    done
done

As an example, if we have a directory tree as follows:

/Applications/
├── Bar.app
│   ├── bar1.png
│   └── bar2.png
├── Foo.app
│   ├── foo1.png
│   └── foo2.png
└── Setup.app
    ├── [email protected]
    ├── IDMusic [email protected]
    └── [email protected]

The result will be:

/Applications/Foo.app => 
  [0] = Applications/Foo.app/foo1.png
  [1] = Applications/Foo.app/foo2.png
/Applications/Bar.app => 
  [0] = Applications/Bar.app/bar1.png
  [1] = Applications/Bar.app/bar2.png
/Applications/Setup.app => 
  [0] = Applications/Setup.app/[email protected]
  [1] = Applications/Setup.app/IDMusic [email protected]
  [2] = Applications/Setup.app/[email protected]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.