user3050705
user3050705

Reputation: 487

Too many arguments in find

#!/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

Answers (6)

gniourf_gniourf
gniourf_gniourf

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

Idriss Neumann
Idriss Neumann

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

Jonathan Leffler
Jonathan Leffler

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

mklement0
mklement0

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

David W.
David W.

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 toxargs. This will give thegrep -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

Michał Kosmulski
Michał Kosmulski

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

Related Questions