Reputation: 487
#!/bin/bash
read DIR
read SEARCH
if [ `find $DIR -type f -exec grep $SEARCH /dev/null {} \;` ]; then
echo "Find"
fi
What is wrong in this code? I have an error: too many arguments
Upvotes: 0
Views: 2850
Reputation: 46833
An answer more about your problem than the original question (as the other answers do address the problem(s) in your question).
It seems you want to prompt the user for a directory to search and a pattern to find in the files that are in the directory. For this, find
is useless as grep
can search recursively in a directory already.
I don't really know what you want to do eventually with this (maybe you should have specified this, so that we don't answer about Y when your original problem is X).
So assuming your problem is1:
How can I prompt a user for a directory and a pattern, and determine whether this pattern is found in at least one of the files contained in the directory (and subdirectories). I don't want to know in which file the pattern is found, only whether it is there or not.
(I'm assuming this is your problem, because this is what your code seems to do in your OP).
To answer this problem:
IFS= read -rp 'Enter a directory: ' dir
IFS= read -rp 'Enter a pattern: ' search
if grep -Rq -- "$search" "$dir"; then
echo "Pattern found"
else
echo "Pattern not found"
fi
By the way this will be much more efficient than any other find
method that don't use the -quit
after the first found file, since grep
with the -q
option will exit immediately after finding the first match.
1It would have been better to state your problem X first, and then show what you attempted (i.e., Y), so that we have a clear picture of both what you want to achieve and how you tried to achieve it. This is a general advice I can give you about asking questions!
Upvotes: 0
Reputation: 3838
[
or [[
or test
aren't a part of if
shell structure but are commands which are provided to do comparisons.
You could try something like :
#!/bin/bash
read DIR
read SEARCH
[[ $(find "$DIR" -type f -exec grep "$SEARCH" {} \;) ]] && echo "Find"
# or
[[ -n $(find "$DIR" -type f -exec grep "$SEARCH" {} \;) ]] && echo "Find"
# or
[[ $(find "$DIR" -type f -exec grep "$SEARCH" {} \;) != "" ]] && echo "Find"
You could also try :
if [[ $(find "$DIR" -type f -exec grep "$SEARCH" {} \;) ]]; then
echo "Find"
fi
Otherwise, do not forget the redirection operator before /dev/null.
Upvotes: 1
Reputation: 754090
When you run:
if [ `find $DIR -type f -exec grep $SEARCH /dev/null {} \;` ]; then
the standard output from find
/grep
is split into words and each word becomes an argument to the [
command. The POSIX version of the [
or test
command has rigid ideas about what it should accept, and the output from your command almost inevitably does not match those ideas, so you get the 'too many arguments' error message.
It isn't entirely clear what you want to do, but you've been given numerous ideas by other people. My best guess is that you need:
if find "$DIR" -type f -exec grep "$SEARCH" /dev/null {} +
then echo "Found"
else echo "Not found"
fi
Note that I've used double quotes around "$DIR"
and "$SEARCH"
; this avoids nasty surprises.
Your use of /dev/null
to ensure the file names are listed is a trick I use too.
Upvotes: 1
Reputation: 438153
In GENERAL:
Always double-quote variable references to protect them from word-splitting and other shell expansions.
To create a conditional based on the exit code of a command, you do NOT need [...]
or [[ ... ]]
, simply use the command AS IS.
Example: To test if any *.txt
file in the current folder contains the word needle
, you could use:
if grep -q "needle" *.txt; then # ...
Note: The -q
suppresses grep
's output; in general, use redirection to suppress unwanted output, such as &> /dev/null
.
SPECIFICALLY:
find
's exit code, when combined with -exec
and terminator \;
, only indicates whether find
itself was invoked successfully, but does NOT reflect the outcome of the commands invoked with -exec
. Thus, the overall command will NOT reflect whether grep
succeeded or not.
If you want to use find
, be sure to terminate your -exec
command with +
rather than \;
- this apparently ensures that the exit code of the command invoked is passed through (verified with GNU find 4.4.2
and the FreeBSD find
version from OS X 10.9.2). With +
, -exec
acts similar to xargs
(by default): the specified command is invoked once with all matching paths (unless all matching paths don't fit on a single command line).
if find "$DIR" -type f -exec grep -q "$SEARCH" {} + ; then # note the `-q`, `+`
echo "Found."
fi
However, in the specific case at hand, you may get away with just grep
itself:
some grep
implementations (e.g., GNU grep
(Linux) and FreeBSD grep
(OSX)) have option -r
(alias: -R
), which searches the specified input directories recursively (use --include <glob>
and --exclude <glob>
to filter by filename pattern):
if grep -q -r "$SEARCH" "$DIR"; then
echo "Found."
fi
Upvotes: 1
Reputation: 107040
I would remove the test all together ( that is the [ ... ]
):
if find "$DIR" -type f -print0 | xargs --null grep -q "$SEARCH"
then
echo "Find"
fi
The find "$DIR" will execute and the results will be passed to
xargs. This will give the
grep -q` statement the files you're passing.
The result of this statement will be the exit status of the last command in the pipe which is the grep
. If grep
finds what you're searching for, it will return an exit code of 0
. Otherwise, it'll return an exit code of non-zero. The if
will pick this up, and will execute the if
part of the statement if grep
returned a zero.
The only issue is that xargs
may execute grep
more than once, and you'll get the status of the last grep
. However, if that happened, then using $(...)
will fail anyway.
Upvotes: 1
Reputation: 10020
Try to just run find $DIR -type f -exec grep $SEARCH /dev/null {} \;
with DIR
and SEARCH
set and see what the output is like. Probably it's quite long, and there are certain limits on command-line lengths. Since you seem to just check that the output is not empty, there is certainly a way of checking that without putting the whole string on the command line - you might be able to just chekc the return code of find
, or as a quick and dirty solution, count the bytes and check if there are more than zero:
if [ `find $DIR -type f -exec grep $SEARCH /dev/null {} \; | wc --bytes` -gt 0 ]; then
echo "Find"
fi
Upvotes: 0