bash commands to print file name and contents

We have a git repo with a ton of projects and each project has an OWNERS file with one line like 'teams/search' with no quotes. I would like to somehow find all those files and create an array of tuples where the tuple is [filename, filecontents].

I can find all files with

find java -name "OWNERS" 

just fine or I can cat the contents like so

find java -name "OWNERS" | cat

How can I create the array though? The next step after that is looping over the array(I think I know how to do this though my bash is rusty) and I can create symlinks by cd to the team directory and ln -s {full file path}


bash data structures are not sophisticated enough to create an array of tuples. You can create an array of strings, or an associative array.

I'm assuming there are no newlines in the pathnames of the OWNERS files.

# an array of OWNERS filenames
mapfile -t owner_files < <(find java -name OWNERS)

# an associative array mapping the filename to the contents
declare -A owner_file_contents
for file in "${owner_files[@]}"; do
    owner_file_contents["$file"]=$(<"file")
done

# inspect the associative array
declare -p owner_file_contents

Bash is very sensitive to filenames with spaces in the name, so you need to quote all the variables.

Some notes:

  • mapfile -t ary < <(some command) -- runs some command in a subshell and read the output into an array, one line per element.

    • This cannot be done with some command | mapfile -t ary because bash runs pipeline commands in separate subshells, which means the array would be created in a subshell, therefore the array will disappear when the subshell exits.
    • refs: mapfile command, 3.5.6 Process Substitution
  • $(<file) is a builtin way to do $(cat file) without having to invoke an external command. Documented in 3.5.4 Command Substitution


Glenn's solution works great! I believe bash updated their data structures back in 2019? (The last I checked) to support mapfile..? It works great with arrays (and with tweaks works with maps as well).

Below is a sample pre-mapfile version that I put together quickly for dumping the content to the array.

array=( $(find find java -name "OWNERS") )

# loop over it
for i in ${array[@]}
do
 file_val= "${i}"
 file_contents="$(cat $i)"
 echo $i: $file_contents
done