0

As far as I know, find . -maxdepth 1 -type f is the only reliable way to get the list of files in a folder. However, naively putting the output of this command in a bash array (($(find . -maxdepth 1 -type f)) is going to fail if some files have spaces in their names.

What is the correct way to do this?

static_rtti
  • 53,760
  • 47
  • 136
  • 192
  • Have you tried setting the IFS (Internal field separator) to `\n`? (i.e.: `IFS=$'\n'`) – keyser Oct 02 '12 at 09:45
  • 1
    `-print0`, null byte separated, to get it in an array see http://stackoverflow.com/questions/1116992/capturing-output-of-find-print0-into-a-bash-array – Wrikken Oct 02 '12 at 09:46
  • If I can avoid playing with the IFS, I'd really appreciate it. – static_rtti Oct 02 '12 at 09:51

3 Answers3

4

The safest way is to use find with -print0 which will also handle filenames with newlines in them correctly. Loop over the files and store them in an array:

declare -a arr=()
while IFS= read -r -d $'\0' f
do
    arr+=("$f")
done < <(find . -maxdepth 1 -type f -print0)

Test:

for i in "${arr[@]}"
do
    echo "[$i]"
done
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • I hate to admit it, but your answer is the best :) Thanks! – static_rtti Oct 02 '12 at 10:04
  • 1
    You can drop the `\0`; either `read` is special-cased to allow `-d ''` to mean the null character, or it is a side effect of null-terminated strings (the first character of the empty string would be its null terminator). `while IFS= read -r -d '' f` – chepner Oct 02 '12 at 12:55
2

This is how I did it the last time I needed to allow spaces:

IFS=$'\n' #Setting the Internal field separator to \n instead of \t\n
array=($(find . -maxdepth 1 -type f))
keyser
  • 18,829
  • 16
  • 59
  • 101
1

You could do this in a cycle:

ARR=()
find . -maxdepth 1 -type f | while read filename; do
    ARR+=("$filename")
done

UPDATE: unfortunately, the while version doesn't work because while runs in a subprocess, with no way of exporting back the results.

So apparently this must be done in a for loop, with no way to avoid manipulating IFS.

The correct answer is dogbane's. The above incorrect method is kept as a warning :-(

LSerni
  • 55,617
  • 10
  • 65
  • 107
  • This assumes that there are no newlines in the filenames, but it's clean and "elegant" (well, for bash it is). If nobody has anything better to suggest, I'm going to pick your answer. – static_rtti Oct 02 '12 at 09:55
  • Actually, your answer doesn't seem to work because the while runs in a child process, so the array is not updated in the parent process. – static_rtti Oct 02 '12 at 09:59
  • In `bash` 4.2 or later, you can set the `lastpipe` option so that the while loop (being the last command of the pipe) executes in the current shell, rather than a subshell. – chepner Oct 02 '12 at 12:56
  • You'd fix the subprocess problem with [Process substitution](http://www.gnu.org/software/bash/manual/bashref.html#Process-Substitution) -- `while ...; done < <(find ...)` – glenn jackman Oct 02 '12 at 13:46