LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 07-28-2009, 06:36 AM   #1
troelskn
LQ Newbie
 
Registered: Apr 2009
Posts: 9

Rep: Reputation: 0
Problem with find: paths must precede expression


I'm trying to write a shell script, and part of its task is to build up arguments to find. But when I try to eval the command string, I get an error. I've isolated the problem to this script:

Code:
#!/bin/sh
FIND_COMMAND="find . \( -name .svn -o -name '*log' -o -name '*txt' -o -name '*xml' -o -name '*xsd' \) -prune -o -print"
$FIND_COMMAND
Which gives me the following output:

Code:
find: paths must precede expression: \)
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
However, if I just run the find command directly, it works fine. What is going on?
 
Old 07-28-2009, 06:47 AM   #2
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Rep: Reputation: 550Reputation: 550Reputation: 550Reputation: 550Reputation: 550Reputation: 550
Works fine for me if I remove the \( and \) ...

Sasha

EDIT -- it APPEARS to work fine: no errors. However, I don't see how the results actually match the search criteria.. It seems to print everything recursively from the CWD..

Last edited by GrapefruiTgirl; 07-28-2009 at 06:51 AM.
 
Old 07-28-2009, 07:01 AM   #3
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Quote:
Originally Posted by GrapefruiTgirl View Post
Works fine for me if I remove the \( and \) ...

Sasha

EDIT -- it APPEARS to work fine: no errors. However, I don't see how the results actually match the search criteria.. It seems to print everything recursively from the CWD..
The command works fine for me, too. Anyway, form the output above it does not look like GNU find, doesn't it?

Sasha, is the action -prune that excludes the names matching the criteria from the print out. The rest is printed out by means of -o -print.
 
Old 07-28-2009, 07:12 AM   #4
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Rep: Reputation: 550Reputation: 550Reputation: 550Reputation: 550Reputation: 550Reputation: 550
OK, thanks Colucix; I looked through the man page but the -prune option did not appear by description to function as it seems to be doing. And I agree, the functionality vs the man page looks odd. The man page claims that brackets () can be used. Maybe the OP needs to escape & quote the expression, inside the quotes defining the variable itself?

From my man page for find:
Code:
 -prune True; if the file is a directory, do not  descend  into  it.  If
              -depth  is  given,  false;  no  effect.  Because -delete implies
              -depth, you cannot usefully use -prune and -delete together.

Last edited by GrapefruiTgirl; 07-28-2009 at 07:15 AM.
 
Old 07-28-2009, 07:20 AM   #5
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Well, usually the -prune action is used in conjunction with -wholename to ignore some directories from the search. An explanation of this behaviour is under the -path option (that is an old predicate for -wholename). Some time ago I wrote a post to try to explain this obscure feature of the find command.
 
Old 07-28-2009, 07:30 AM   #6
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Quote:
Originally Posted by GrapefruiTgirl View Post
The man page claims that brackets () can be used. Maybe the OP needs to escape & quote the expression, inside the quotes defining the variable itself?
I don't know what the exact problem is, maybe the shell is confused by so many quotes and wildcards (???) but adding eval before $FIND_COMMAND solves the issue. The problem is not in the find command itself, but in the way it is executed:
Code:
$ bash -x test.sh   ### without eval
+ FIND_COMMAND='find . \( -name .svn -o -name '\''*log'\'' -o -name '\''*txt'\'' -o -name '\''*xml'\'' -o -name '\      ''*xsd'\'' \) -prune -o -print'
+ find . '\(' -name .svn -o -name ''\''*log'\''' -o -name ''\''*txt'\''' -o -name ''\''*xml'\''' -o -name ''\''*xs      d'\''' '\)' -prune -o -print
find: paths must precede expression: \)
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
$
$ bash -x test.sh   ### with eval
+ FIND_COMMAND='find . \( -name .svn -o -name '\''*log'\'' -o -name '\''*txt'\'' -o -name '\''*xml'\'' -o -name '\''*xsd'\'' \) -prune -o -print'
+ eval find . '\(' -name .svn -o -name ''\''*log'\''' -o -name ''\''*txt'\''' -o -name ''\''*xml'\''' -o -name ''\''*xsd'\''' '\)' -prune -o -print
++ find . '(' -name .svn -o -name '*log' -o -name '*txt' -o -name '*xml' -o -name '*xsd' ')' -prune -o -print
.
./files
./files/paperino
./files/topolino
./test.sh
 
Old 07-28-2009, 07:50 AM   #7
GrapefruiTgirl
Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Rep: Reputation: 550Reputation: 550Reputation: 550Reputation: 550Reputation: 550Reputation: 550
Interesting, that the OP does use the word 'eval' too, yet it is not included in his OP. Hopefully your above post helps him/makes it work for him (or her) too.

S
 
Old 07-28-2009, 08:06 AM   #8
troelskn
LQ Newbie
 
Registered: Apr 2009
Posts: 9

Original Poster
Rep: Reputation: 0
Thanks. eval seems to make it work.

For the record, I tried various forms of escaping, and I couldn't make it work in neither sh or bash. I still don't understand what's wrong, but at least my immediate problem is solved.
 
Old 07-28-2009, 08:10 AM   #9
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,563
Blog Entries: 29

Rep: Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179
Hello troelskn
Quote:
Originally Posted by troelskn View Post
However, if I just run the find command directly, it works fine. What is going on?
When you run the command directly the shell evaluates \( to ( and \) to ) thus passing
Code:
. ( -name .svn -o -name '*log' -o -name '*txt' -o -name '*xml' -o -name '*xsd' ) -prune -o -print
to the find command and not starting a subshell for a list of commands (http://www.gnu.org/software/bash/man...mmand-Grouping).

When you run the command by $FIND_COMMAND the shell does not do that evaluation and this is what is passed to find
Code:
. \( -name .svn -o -name '*log' -o -name '*txt' -o -name '*xml' -o -name '*xsd' \) -prune -o -print
With reference to the find syntax, find takes everything on the command line as a path until the expression begins. \( is not an expression element so find is going to search both . and \(. Find continues parsing its arguments and happily munches everything as parts of expression until it gets to \) which is not an expression element. Time for an error message. What error message? Well, \) ain't an option or an expression element then it must be a path. Hence "paths must precede expression: \)"

As already posted, this works:
Code:
FIND_COMMAND="find . ( <expression elements> ) -prune -o -print"
$FIND_COMMAND
but does it work as intended? In the same way that \ is used to prevent the shell acting on ( and ), the single quotes are used to prevent the shell doing filename expansion (more beautifully called globbing!) on the likes of *log.

That's essential on the command line, in case there are any *log files-or-directories in the current directory. But it's *log etc. you want to pass to find. I think the revised value of FIND_COMMAND above actually passes '*log' to find. "find" will dutifully look for files called '<something-or-nothing>log' with the quotes and not match the files you want it to match.

Two ways to do what you want
Code:
FIND_COMMAND="find . \( -name .svn -o -name '*log' -o -name '*txt' -o -name '*xml' -o -name '*xsd' \) -prune -o -print"
eval $FIND_COMMAND
eval makes an extra evaluation pass that removes the \ in front of ( and ) and removes the single quotes. It's quite slow but that may not matter.

Alternatively
Code:
FIND_COMMAND="find . ( -name .svn -o -name *log -o -name *txt -o -name *xml -o -name *xsd ) -prune -o -print"
set -f
$FIND_COMMAND
set +f
Here (not tested) "set -f" turns off globbing so *log is passed to find, regardless of any *log files-or-directories in the current directory.

Best

Charles
 
Old 07-28-2009, 08:24 AM   #10
troelskn
LQ Newbie
 
Registered: Apr 2009
Posts: 9

Original Poster
Rep: Reputation: 0
Thanks for the explanation - I think I got most of it, although I don't understand why it's necessary to turn off globbing while running $FIND_COMMAND? Why are the wildcards expanded in that case?
 
Old 07-28-2009, 01:59 PM   #11
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,563
Blog Entries: 29

Rep: Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179
Quote:
Originally Posted by troelskn View Post
Thanks for the explanation - I think I got most of it, although I don't understand why it's necessary to turn off globbing while running $FIND_COMMAND? Why are the wildcards expanded in that case?
Because the second last step in the shell's expansions is to do globbing of any strings containing glob specials *, ? and [<stuff>]. Try this
Code:
x='*'
echo $x
For the full picture of how bash handles a command, see GNU Bash Reference, ShellOperation. It is the fourth step of that process that is relevant, detailed in GNU Bash Reference, Shell-Expansions where it is the 8th step that applies. Dontcha just love bash?!

Last edited by catkin; 07-28-2009 at 02:00 PM. Reason: Forgot to quote * in x='*'
 
Old 07-29-2009, 03:48 AM   #12
troelskn
LQ Newbie
 
Registered: Apr 2009
Posts: 9

Original Poster
Rep: Reputation: 0
Quote:
Originally Posted by catkin View Post
For the full picture of how bash handles a command, see GNU Bash Reference, ShellOperation. It is the fourth step of that process that is relevant, detailed in GNU Bash Reference, Shell-Expansions where it is the 8th step that applies. Dontcha just love bash?!
Whoa .. That is not intuitive.

So let me just verify that I got this right: The shell consists of a reader and a runtime environment, and both makes expansions. If I pass a variable to eval, it will run through both components, but if I simply run the command within a shell script, it only goes through the latter?
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
find: paths must precede expression lead2gold Linux - General 11 02-27-2014 08:06 AM
awk: how to find expression in a certain field? Micro420 Programming 5 08-08-2007 06:59 PM
Algorithm to find paths xeon123 Programming 5 04-24-2007 03:18 AM
Wine can't find paths in Mandriva 2005 LE kalahari875 Mandriva 3 06-28-2005 08:22 AM
'find' expression question Thorsten Linux - General 3 05-03-2004 08:41 AM


All times are GMT -5. The time now is 07:31 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration