使用 shell 变量执行命令

使用 shell 变量执行命令

我在变量中有一个 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]

当我尝试$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]

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

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

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

答案1

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

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

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

另一个大错误是您将其用作`$cmd`唯一的命令行。如果$cmd(即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

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

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

或者你可以完全这样做grep

grep --recursive --exclude-dir=.svn pattern path

答案2

如果您确实想在变量中执行代码,可以使用eval.

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

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

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

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

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

然后这样称呼它

webgrep PATTERN

答案3

尝试像这样构建变量:

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

或者

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

或者别名可能更适合:

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

答案4

根据经验,将代码放入变量中。使用一个函数。

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

cmd "$@"

如果你最终不得不把简单的代码在变量中(不要,但如果你这样做......)那么如上所述,你不能嵌入管道或重定向之类的东西而没有eval......并且也不要这样做,哈哈

但不要在反引号中执行它,反引号会在子 shell 中运行命令并将其替换为输出,以便输出成为解析器作为要执行的命令接收的内容:

$: `echo foo`
bash: foo: command not found

相关内容