Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a unix command in a variable, it looks like this:

cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep $@"
`$cmd`
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

When I try to execute the command $cmd in a bash script, it won't work, however, when I copy and paste the exact same command, it does work. Can you let me know what I am doing wrong?

I have tried putting quotes around the path, same error occurs

cmd="find \"/path/to/webpage\" -type f | grep -v .svn | xargs grep $@"
find: paths must precede expression
Usage: find [-H] [-L] [-P] [path...] [expression]

When I remove the -type f parameter, I get this error:

cmd="find /path/to/webpage | grep -v .svn | xargs grep $@"
find: invalid predicate `-v'

That makes me think that the pipe is not being recognized. What can I do to get this to work?

share|improve this question
1  
Long command pipelines in shell variables should make anyone nervous. What are you trying to do? I recommend taking some time to read and digest BashFAQ 50 before taking this approach any further. –  jw013 Jul 26 '12 at 20:19
    
are you trying to execute the contents of a variable or assign the output of a command to a variable? Your question says the former, but your response to Tim's answer says the latter. –  cas Jul 26 '12 at 22:25

3 Answers 3

Others have already explained what to do. Let me explain what's happening here: the pipe character | doesn't make a pipeline as the variable is expanded, but acts like a literal character. Therefore, find is executed with the following arguments:

{"/path/to/webpage", "-type", "f", "|", "grep", "-v", ".svn", "|", ...}

and it interprets the | as a path and complains that it should have appeared before the expression (-type f).

Another big mistake is that you're using `$cmd` as the sole command line. If $cmd (i.e. find ...) succeeded and produced output like rm -rf /, it would be executed on your behalf. Always take caution when you take data as code!

Improvement 1. find ... | grep -v ... is a poor way to exclude something from the output: find will traverse whole subdirectories named .svn, produce the lines, only to be thrown away later. Why not tell find to do it directly?

find path -type f | grep -v .svn                # don't do this
find path -name .svn -prune -o -type f -print   # do this instead

Improvement 2. When combining find and xargs, always use -print0 in find and -0 in xargs:

find path ... -print0 | xargs -0 -r grep ...    # I'd also recommend -r

or you can do it entirely in grep:

grep --recursive --exclude-dir=.svn pattern path
share|improve this answer

If you really want to execute code in a variable, you can do it using eval.

cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep something"
eval "$cmd"

But since you're trying to pass arguments in with $@, what you need here is a function

webgrep() {
    find /path/to/webpage -type f | grep -v .svn | xargs grep "$@"
}

Note that this will have problems with any paths that include a whitespace character. And it will have to scan all the contents of .svn directories before ignoring them. A better way is

webgrep() {
    find /path/to/webpage -name .svn -prune -o -type f -exec grep "$@" {} +
}

Then call it like this

webgrep PATTERN
share|improve this answer
    
+1 use a function. –  glenn jackman Jul 26 '12 at 23:08

Try building your variable like this:

cmd=$(find /path/to/webpage -type f | grep -v .svn | xargs grep $@)

or

cmd=`find /path/to/webpage -type f | grep -v .svn | xargs grep $@`

Or maybe an alias would be better suited:

alias cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep $@"
share|improve this answer
    
Thank you, using the $(command) method is what did the trick! –  Roy Rico Jul 26 '12 at 19:38
    
No problem! You caught me mid-edit, but should be fixed now. –  Tim Jul 26 '12 at 19:40
3  
That's not putting the code in a variable. It's putting the output of the command in a variable. –  Mikel Jul 26 '12 at 21:27

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.