2

I'm trying to build a basic REPL in bash. The script dynamically populates a list of files in a directory for the user to run.

File space:

|
|\ scripts/
|| script1.sh
|| script2.sh
|
\  shell/
 | shell.bashrc
 | shell.desktop

From shell.bashrc, I'm using the following command to get an array of filenames:

readarray -d " " filenames < <(find ../bin -type f -executable)

I can print the array filenames just fine and it contains a space separated string that holds "script1.sh script2.sh" as expected.

But when I try to access the first element of the array with echo ${filenames[0]} it prints every element. Any other index besides 0 returns an empty string.

My bash version is 5.0.17, and the first line of the file is #!/bin/bash

I moved to using "readarray" after trying the following led to similar results:

filenames=($(find "../bin" -type f -executable))

Edit: Found a dumb workaround and would still like to know where the original post is messing up. Workaround:

readarray -d " " filenames < <(find ../bin -type f -executable)
arr=($filenames)
echo ${arr[1]}

Which prints the 6th element of the array as expected.

1

1 Answer 1

2

By default, find outputs results separated by newlines. By setting -d " " in the mapfile/readarray command, you are causing (assuming none of the names contains a space character) all of the results to be concatenated into a single string - newlines and all. When you then echo ${filenames[0]} (with unquoted variable expansion ${filenames[0]} and the default space-tab-newline value of IFS), the shell splits on newline, and echo reassembles the result using spaces1.

Instead use

readarray -t filenames < <(find ../bin -type f -executable)

which will parse the input as newline separated data, but strip the trailing newlines from the stored elements. Or - better - if your bash version supports it,

readarray -t -d '' filenames < <(find ../bin -type f -executable -print0)

which uses null bytes instead of newlines (making it safe for all legal filenames, even those that contain newlines).


1 See When is double-quoting necessary?

3
  • 1
    Using variables without double-quotes (e.g. echo ${filenames[0]} instead of echo "${filenames[0]}") is almost always a mistake (see here and here). Especially for arrays, declare -p filename is often even more helpful at figuring out what's actually stored in a variable. Jul 13, 2021 at 19:23
  • Thank you! Both solutions worked like a charm. I'm glad that the solution was a bit more than a syntax error haha. The reason my workaround worked then is because in the reassembling of filenames[0], it was a space separated string as expected for the indexing, right? Jul 13, 2021 at 19:28
  • @user3116064 almost - it's really because $filenames is equivalent to ${filenames[0]} and the (...) array constructor uses the same space-tab-newline IFS value, so it doesn't matter which particular whitespace character is the delimiter. Jul 13, 2021 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.