1

I am dealing with paths that will have spaces.

I am globbing this way (the second line sets up the glob):

IFS=$'\n'

VAR="$1/"name_*

for file in $VAR; do
  echo $file
  grep ITEM "$file" | wc -l
done

I need to look only in files that are named name_* under $1. The IFS I set here lets me look at the files properly, because it prevents the for from becoming tripped up by spaces in filenames.

However, I now want an easy way to grab the total number of such files matched by the glob. I know I can use a counter in the for loop, but I was hoping I could use a pipe with my VAR to do this.

However, if I echo $VAR, the globbing occurs successfully, but the different paths are joined by space, which ruins me because I can now no longer separate the items... Is there a way to override this behavior similar to how IFS works on for?

Steven Lu
  • 2,282
  • Another approach that satisfies the requirement but without directly answering the question is to filter (using grep or whatnot) the output of ls or whatnot, rather than to glob the path. – Steven Lu Oct 02 '18 at 02:41

2 Answers2

3

You should avoid using/expanding strings if what you want is a list of separated values.

The basic solution is to set the positional parameters:

set -- "$1"/name_*

That will keep each matched file in one separated positional parameter even with spaces or newlines (or most other characters).

In bash, you should set shopt -s failglob to make the script stop if no file is matched by the glob (*), or shopt -s nullglob if you want to get no result (as opposed to the glob itself "$1"/name_*) if the glob fails to match any file. Keep failglob unset to avoid stopping the script.

The count of files (count the number of matches of a glob) is now simply:

echo "$#"

the count of positional parameters.

The for loop would reduce to:

for file
do  echo "$file"
done

That completely avoid problems with splitting on IFS.

Note that external values $1 must be quoted to avoid code injection.
As also does the echo "$file" should be quoted.

It is also possible to assign a list to an array:

files=( "$1"/name_* )

That will avoid clobbering the positional parameters, but will make the syntax a bit more complex. The count of elements in the array is:

echo "${#files[@]}"

And the loop will need some change:

for file in "${files[@]}"; do
    echo "$file"
done
1

You could solve the problem of spaces and get the count if you switched to using arrays:

VAR=("$1/"name_*) # make array of filenames matching glob

echo "${#VAR[@]}" # number of elements in array

for file in "${VAR[@]}"; do  # loop over individual elements of array
  echo "$file"
  grep ITEM "$file" -c  # grep can count, wc isn't needed
done
Olorin
  • 4,656