The code you showed is:
#!/bin/bash
count=0
for i in $@; do
if [ grep '$1' $i ];then
((count++))
fi
done
echo "$1 $count"
When I run it, I get the error:
script.sh: line 5: [: $1: binary operator expected
This is reasonable, but it is not the same as either of the errors reported in the question. There are multiple problems in the code.
The for i in $@; do
should be for i in "$@"; do
. Always use "$@"
so that any spaces in the arguments are preserved. If none of your file names contain spaces or tabs, it is not critical, but it is a good habit to get into. (See How to iterate over arguments in bash script for more information.)
The if
operations runs the [
(aka test
) command, which is actually a shell built-in as well as a binary in /bin
or /usr/bin
. The use of single quotes around '$1'
means that the value is not expanded, and the command sees its arguments as:
[
grep
$1
current-file-name
]
where the first is the command name, or argv[0]
in C, or $0
in shell. The error I got is because the test
command expects an operator such as =
or -lt
at the point where $1
appears (that is, it expects a binary operator, not $1
, hence the message).
You actually want to test whether grep
found the word in $1
in each file (the names listed after $1
). You probably want to code it like this, then:
#!/bin/bash
word="$1"
shift
count=0
for file in "$@"
do
if grep -l "$word" "$file" >/dev/null 2>&1
then ((count++))
fi
done
echo "$word $count"
We can negotiate on the options and I/O redirections used with grep
. The POSIX grep
options -q
and/or -s
options provide varying degrees of silence and -q
could be used in place of -l
. The -l
option simply lists the file name if the word is found, and stops scanning on the first occurrence. The I/O redirection ensures that errors are thrown away, but the test ensures that successful matches are counted.
Incorrect output claimed
It has been claimed that the code above does not produce the correct answer. Here's the test I performed:
$ echo "This country is young" > young.iii
$ echo "This country is little" > little.iii
$ echo "This fruit is fresh" > fresh.txt
$ bash findit.sh country young.iii fresh.txt little.iii
country 2
$ bash -x findit.sh country young.iii fresh.txt little.iii
+ '[' -f /etc/bashrc ']'
+ . /etc/bashrc
++ '[' -z '' ']'
++ return
+ alias 'r=fc -e -'
+ word=country
+ shift
+ count=0
+ for file in '"$@"'
+ grep -l country young.iii
+ (( count++ ))
+ for file in '"$@"'
+ grep -l country fresh.txt
+ for file in '"$@"'
+ grep -l country little.iii
+ (( count++ ))
+ echo 'country 2'
country 2
$
This shows that for the given files, the output is correct on my machine (Mac OS X 10.10.2; GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14)). If the equivalent test works differently on your machine, then (a) please identify the machine and the version of Bash (bash --version
), and (b) please update the question with the output you see from bash -x findit.sh country young.iii fresh.txt little.iii
. You may want to create a sub-directory (such as junk
), and copy findit.sh
into that directory before creating the files as shown, etc.
You could also bolster your case by showing the output of:
$ grep country young.iii fresh.txt little.iii
young.iii:This country is young
little.iii:This country is little
$