Javi
Javi

Reputation: 3610

Number of elements returned by find in bash script

I have the following bash script:

#!/bin/bash

test=$(find . -name "*.cfg")
echo ${#test[@]}

And the output is simply 1. However, find returns 8 elements and when used in a loop their names are displayed correctly:

for f in $(find . -name "*.cfg")
do
    echo $f
done

How can I check the size of the test array? Thank you!

Upvotes: 2

Views: 251

Answers (4)

gniourf_gniourf
gniourf_gniourf

Reputation: 46823

The only safe way to store the output of find into an array is to use the -print0 extension, which is not POSIX (yet, GNU find supports it):

files=()
while IFS= read -r -d '' file; do
    files+=( "$file" )
done < <(
    find . -name '*.cfg' -print0
)
printf 'There are %d files found.\n' "${#files[@]}"

If you only want the number of files found, you can use this (using -printf, not POSIX but supported by GNU):

xfiles=$(find . -name '*.cfg' -printf 'x')
printf 'There are %d files found.\n' "${#xfiles}"

or in a POSIX-compliant way:

xfiles=$(find . -name '*.cfg' -exec printf "%.sx" {} +)
printf 'There are %d files found.\n' "${#xfiles}"

Now Bash≥4 can deal with this on its own:

shopt -s globstar nullglob
files=( **/*.cfg )
printf 'There are %d files found.\n' "${#files[@]}"

(though it might be slower than find).

Upvotes: 3

chepner
chepner

Reputation: 531075

Since you aren't using any other feature of find, I would use a glob to create the array instead:

# Bash 4+
shopt -s globstar
test=( **/*.cfg )

The globstar option enables the pattern **, which matches 0 or more directories in a path. It allows for recursive matching with a directory hierarchy, so **/*.cfg would match ./foo.cfg, ./bar/hello world.cfg, etc. Each matched file, regardless of whatever special characters may exist in the file name, will be treated as a single array entry.

If you need to support an older version of bash, there are more complicated techniques you can use assuming your version of find supports -print0 or something similar.

Upvotes: 2

Etan Reisner
Etan Reisner

Reputation: 80931

test is a string (not an array).

You are using [@] on it which isn't doing anything useful and is breaking things for you. Drop it.

You want ${#test} to get the length of the string. If you want an array you need to do that differently.

You do not want to parse/read/for-loop/etc. over the output of find that way though (it isn't safe for "odd" file names).

See http://mywiki.wooledge.org/BashFAQ/001 for ways to do that safely (specifically see the section about -print0).

Upvotes: 2

Amit
Amit

Reputation: 20456

You have to create an array first, try this:

test=($(find . -name "*.cfg"))
echo ${#test[@]}

Upvotes: -1

Related Questions