使用 shell 变量执行命令

Roy*_*ico 4 bash find

我在一个变量中有一个 unix 命令,它看起来像这样:

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]
Run Code Online (Sandbox Code Playgroud)

当我尝试$cmd在 bash 脚本中执行命令时,它不起作用。但是,当我复制并粘贴完全相同的命令时,它确实有效。你能告诉我我做错了什么吗?

我试过在路径周围加上引号,发生同样的错误

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

当我删除-type f参数时,出现此错误:

cmd="find /path/to/webpage | grep -v .svn | xargs grep $@"
find: invalid predicate `-v'
Run Code Online (Sandbox Code Playgroud)

这让我认为管道没有被识别。我该怎么做才能让它发挥作用?

mus*_*hil 7

其他人已经解释了该怎么做。让我解释一下这里发生的事情:管道字符|不会在变量扩展时形成管道,而是像文字字符一样起作用。因此,find使用以下参数执行:

{"/path/to/webpage", "-type", "f", "|", "grep", "-v", ".svn", "|", ...}
Run Code Online (Sandbox Code Playgroud)

并将 the 解释|为路径并抱怨它应该出现在表达式 ( -type f)之前。

另一个大错误是您将其`$cmd`用作唯一的命令行。如果$cmd(ie find ...) 成功并产生类似 的输出rm -rf /,它将代表您执行。当您将数据作为代码时,请务必小心!

改进 1. find ... | grep -v ...是一种从输出中排除某些内容的糟糕方法:find将遍历名为 的整个子目录.svn,生成行,稍后将被丢弃。为什么不find直接告诉做呢?

find path -type f | grep -v .svn                # don't do this
find path -name .svn -prune -o -type f -print   # do this instead
Run Code Online (Sandbox Code Playgroud)

改进 2.组合findand 时xargs,始终使用-print0infind-0in xargs

find path ... -print0 | xargs -0 -r grep ...    # I'd also recommend -r
Run Code Online (Sandbox Code Playgroud)

或者你可以完全在grep

grep --recursive --exclude-dir=.svn pattern path
Run Code Online (Sandbox Code Playgroud)


Mik*_*kel 5

如果你真的想在一个变量中执行代码,你可以使用eval.

cmd="find /path/to/webpage -type f | grep -v .svn | xargs grep something"
eval "$cmd"
Run Code Online (Sandbox Code Playgroud)

但是由于您尝试使用 with 传递参数$@,因此您需要的是一个函数

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

请注意,任何包含空格字符的路径都会出现问题。或以减号开头的模式。它必须先扫描.svn目录的所有内容,然后再忽略它们。处理用户意外传递多个参数会很好(例如,因为模式没有正确引用)。更好的方法是

webgrep() {
    find /path/to/webpage -name .svn -prune -o -type f -exec grep -e "$*" {} +
}
Run Code Online (Sandbox Code Playgroud)

然后这样调用

webgrep PATTERN
Run Code Online (Sandbox Code Playgroud)