LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   find: paths must precede expression -- already checked google/man/faqs (http://www.linuxquestions.org/questions/programming-9/find-paths-must-precede-expression-already-checked-google-man-faqs-792535/)

escalf 03-02-2010 02:18 AM

find: paths must precede expression -- already checked google/man/faqs
 
Running into a little bit of an issue with find in my script...
When running the find via script, I get
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

However, when running from cli, it runs without issue.
My first thought - along with everything else I've seen on this issue across google, and linuxquestions - was escapism. No such luck. All wildcards as in 'style' quotes, and I've tried using \* style escaped wildcards to no avail.

I have things set up a little weird, so that may be the issue. Since this script is one that will ultimately be maintained and edited by a number of people, I've set it up so the primary search strings can be easily changed by simply adding to an array, The script then concatenates everything down into one large variable that contains the singular search string for the find command. I'll show the code for all of that, below...

First, the beginning of everything...
Code:

declare -rx CHECK_LIST_FIRST='*.mp3'
declare -a CHECK_LIST
declare -rx CHECK_LIST=(
        '*.divx'
        '*crack*'
        '*keygen*'
        '*serial*'
        '*.torrent'
        '*.xvid'
        '*.dvdr*'
        '*dtv'
        '*.720p'
        '*.1080*'
        '*.rar'
)
declare -x SEARCHTERMS

We go through a little sanity checking and misc whatnot, and then concatenate the array down to a single var:
Code:

TERMCOUNTER=1
        declare -a TERMSARRAY
        for CHECKTERM in `echo ${CHECK_LIST[*]}`
        do
        TERMSARRAY[${TERMCOUNTER}]="-or -iname '${CHECKTERM}' -printf '%u \n'"
        let "TERMCOUNTER = $TERMCOUNTER + 1"
        done
        SEARCHTERMS2=`echo ${TERMSARRAY[*]}`
        SEARCHTERMS="-iname '${CHECK_LIST_FIRST}' -printf '%u \n' ${SEARCHTERMS2}"

Then we get on with our search:
Code:

ionice -n4 -c 2 find /home -path /home/virtfs -prune -o -type f ${SEARCHTERMS} >> /root/results.find
Here's some debugging output...
From when the array is being created, this is $SEARCHTERMS once it's complete:
Code:

-iname '*.mp3' -printf '%u \n' -or -iname '*.divx' -printf '%u \n' -or -iname '*crack*' -printf '%u \n' -or -iname '*keygen*' -printf '%u \n' -or -iname '*serial*' -printf '%u \n' -or -iname '*.torrent' -printf '%u \n' -or -iname '*.xvid' -printf '%u \n' -or -iname '*.dvdr*' -printf '%u \n' -or -iname '*dtv' -printf '%u \n' -or -iname '*.720p' -printf '%u \n' -or -iname '*.1080*' -printf '%u \n' -or -iname '*.rar' -printf '%u \n'
and here's the find command echo'ed:
Code:

ionice -n4 -c 2 find /home -path /home/virtfs -prune -o -type f -iname '*.mp3' -printf '%u \n' -or -iname '*.divx' -printf '%u \n' -or -iname '*crack*' -printf '%u \n' -or -iname '*keygen*' -printf '%u \n' -or -iname '*serial*' -printf '%u \n' -or -iname '*.torrent' -printf '%u \n' -or -iname '*.xvid' -printf '%u \n' -or -iname '*.dvdr*' -printf '%u \n' -or -iname '*dtv' -printf '%u \n' -or -iname '*.720p' -printf '%u \n' -or -iname '*.1080*' -printf '%u \n' -or -iname '*.rar' -printf '%u \n' >> /root/results.find
Ergo, we should be in a disco ballroom somewhere, getting down. Instead, I get:
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

...what, praytheetell, am I missing?

(Muchas Danke Comrades)

-A very flustered, and fairly annoyed at everything bash and find,
--Eric

gnashley 03-02-2010 02:48 AM

For starters, I think you need to use double-quotes around wildcards:
"*.divx" -printf '%u \n' -or -iname "*crack*"

escalf 03-02-2010 03:19 AM

Quote:

Originally Posted by gnashley (Post 3882096)
For starters, I think you need to use double-quotes around wildcards:
"*.divx" -printf '%u \n' -or -iname "*crack*"

I started out that way (using double quotes). A lot of threads I found said that due to the way things are eval'd in script, you needed to use single quotes. So, I tried single. Haven't reverted yet.
Works fine either way at cli.

colucix 03-02-2010 06:01 AM

Maybe putting an eval in front of the ionice command solves the problem. However I'd do something slightly different:

1) to apply multiple logical OR to the action alternative to -prune, better to embed them within parentheses, e.g.
Code:

find /path/to/search -wholename /path/to/exclude -prune -o \( multiple expressions here \) -print
otherwise the command is garbled. Moreover, in this way you can avoid to put multiple -printf actions.

2) when the shell applies variable expansion, in most cases the resulting "expanded" string is left untouched. This means that you don't really need so many single quotes and/or escape characters, otherwise the shell is garbled now. I'd remove the single quotes from the value of the variable SEARCHTERMS, so that the resulting code could be something like this (the declaration part being the same)
Code:

TERMCOUNTER=1
declare -a TERMSARRAY
for CHECKTERM in `echo ${CHECK_LIST[*]}`
do
  TERMSARRAY[${TERMCOUNTER}]="-o -iname ${CHECKTERM}"
  let "TERMCOUNTER = $TERMCOUNTER + 1"
done
SEARCHTERMS2=`echo ${TERMSARRAY[*]}`
SEARCHTERMS="( -iname ${CHECK_LIST_FIRST} ${SEARCHTERMS2} )"

ionice -n4 -c2 find /home -wholename /home/virtfs -prune -o -type f ${SEARCHTERMS} -printf "%u\n"

Last note: be sure you don't put the script in a directory that contains also some files that match any of the search patterns. If this is the case, the pattern (*.rar for example) will be expanded to that name inside the for loop. The resulting find command will have something like "-iname somefile.rar" instead of "-iname *.rar" and it will miss some results.

catkin 03-02-2010 10:02 AM

Quote:

Originally Posted by escalf (Post 3882134)
I started out that way (using double quotes). A lot of threads I found said that due to the way things are eval'd in script, you needed to use single quotes. So, I tried single. Haven't reverted yet.
Works fine either way at cli.

Single quotes are functionally identical to double quotes as long as the quoted string does not contain:
  • $, `, \, and, when history expansion is enabled, !. Full details here.
  • any single quotes
Using either single or double quotes avoids the problem colucix mentioned of "the pattern (*.rar for example) will be expanded to that name inside the for loop".

Using ionice results in the find command strings being expanded twice in which case you need to quote within quotes to generate the desired command string, for example
Code:

ionice -n4 -c 2 find /home -path /home/virtfs -prune -o -type f "'${SEARCHTERMS}'" >> /root/results.find
The double quotes allow ${SEARCHTERMS} to be expanded during the first expansion; the single quotes ensure that the expansion of ${SEARCHTERMS} remains as a single word (and not subject to filename expansion) during the second expansion.

Incidentally the {} in ${SEARCHTERMS} is not necessary; $SEARCHTERMS would be sufficient.

grail 03-02-2010 10:12 AM

I am curious, as not being a find guru or anything, but as you have already gone to the trouble of iterating
through your array of search items, why not just do the find in the loop?

Please don't bash me for asking, but I was just wondering if this is better than the overhead of what I have
mentioned?

Enquiring mind


All times are GMT -5. The time now is 06:26 AM.