I would like to count the number of *.sav files in the $1 folder.
I tried to use the following command but it doesn't work, what is the problem?
numOfFiles=`ls $1 | grep -o *\.sav | wc -w`
Do not parse the output from ls
Use bash arrays instead.
shopt -s nullglob
sav_files=(*.sav)
echo ${#sav_files[@]}
.sav files though (and also, you have spaces around the =).shopt -s nullglobThe main problem is that *.sav is not a regular expression, it's a glob. You likely wanted to grep for \.sav, which you have to escape once for the shell and once again because of the antiquated `` syntax:
numOfFiles=`ls $1 | grep -o \\\\.sav | wc -w`
Additionally, you should not parse the output of ls, and wc -w will cause filenames with spaces to be counted multiple times.
A better solution would be to use find:
numOfFiles=$(find "$1" -maxdepth 1 -name '*.sav' | wc -l)
For an even better, Bash specific solution, see 1_CR's answer.
grep argument would behave the same even if you used $(...)Don't want search hypothetical problems in file counting, but parsing output from ls can be sometimes wrong idea. Try it yourself, create a file:
touch 'aa bb
cc.sav'
so, create file with space and newline in its name. Usually any ls parsing will fail.
The 1_CR's solution is NICE and ELEGANT.
If you for some reason don't like it, you can use find and don't use wc because wc for example fails is the last line doesn't contain newline. (wc don't count lines but \n chars.
The next will give correct answer:
numOfFiles=$(find . -maxdepth 1 -name \*.sav -print0 | grep -zc .)
the find with -print0 prints all found files "zero ended", and grep -z can read like zero delimited "lines". the -c simple count found lines for the pattern . (any char)
Your regex is wrong, * is a repetition operator to grep but you don't specify what to repeat.
Anyway, the grep is unnecessary; just specify the pattern as a shell glob.
numOfFiles=`ls "$1"/*.sav | wc -l`
Note the quotes around $1 and the use of line count rather than word count; wc -w would return a count of two for underscore_filename.
If you do use grep for something, it's usually a good idea to put the search pattern in single quotes, so you don't have to backslash-escape shell metacharacters. (Even with the backslash, your pattern would end up unescaped because the shell eats one level of escapes in unquoted strings.)
If you have any matches on *.sav in the current directory, the unquoted regex would be expanded as a glob by the shell, and produce interesting results which could be very hard to debug.
Personally I would recommend using quotes around the variables to prevent spaces being a problem.
And additionally, wc -l is a better solution since it counts lines instead of words (files with spaces might count twice).
So try this instead:
numOfFiles=$(ls -1 "$1/"*.sav | wc -l)
"$1"/*.sav