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,175
  • 3
  • 24
  • 37
  • 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,636
  • 1
  • 13
  • 25