传递带有参数的命令并重定向到函数

传递带有参数的命令并重定向到函数

在 Bash 脚本中,我传递带有参数的命令。除非命令包含重定向,否则这工作正常。在这种情况下,重定向字符被视为普通字符。

$ cat foo
#!/bin/bash

f() {
  echo "command: $@"
  $@
}

f echo a-one a-two
f 'echo b-one b-two'
f 'echo c-one c-two > c.tmp'
# I don't want to do f echo d-one d-two > d.tmp because I want to redirect the
# output of the passed command, not the output of the f() function.

$ ./foo
command: echo a-one a-two
a-one a-two
command: echo b-one b-two
b-one b-two
command: echo c-one c-two > c.tmp
c-one c-two > c.tmp

如您所见,当我想将“c-one c-two”打印到文件 c.tmp 时,这将打印“c-one c-two > c.tmp”。有没有办法做到这一点?

答案1

f() {
  echo "command: $@"
  $@
}

一般来说,你应该使用"$@",这里也为通常 原因

但你是对的,运行这样的命令不会处理 shell 操作符,无论是重定向还是&&.您可以将该函数更改为其eval获取的命令,这将处理重定向,但也可以处理其他所有内容。

特别是,传递任意文件名将再次变得困难。例如,这最终将ls在两个文件上运行foobar

run_with_eval() {
    eval "$@"
}
file='foo bar'
run_with_eval ls -l "$file" 

你必须安排文件名被引用对于 shell 来说,这有点尴尬并且容易出错。

但如果你仅有的想要重定向,甚至更好,只输出重定向>,你可以让函数手动处理它:

#!/bin/bash
run_with_redir() {
    local redir=
    if [[ $1 = '>' ]]; then
        redir=$2
        shift 2
    fi
    ## whatever else you do
    if [[ $redir ]]; then
        "$@" > "$redir"
    else
        "$@"
    fi
}

run_with_redir echo "normal command, no redirection"
run_with_redir ">" output.txt echo "this gets redirected to output.txt"

也就是说,使用>文件名作为前两个参数调用函数,将其余参数作为命令运行,并重定向输出。如果没有>,它会正常运行命令。请注意,">"调用函数时必须加引号,否则我们将再次获得整个函数的输出重定向。将其扩展为支持eg>>应该很简单,即使是重复的。

这也应该适用于更棘手的情况:

run_with_redir ">" "output file with space.txt" ls -l "other file with space"

答案2

eval "$@"会做这个工作:

#!/bin/bash

f() {
  echo "command: $@"
  eval "$@"
}

但总的来说,即使你会看到它,使用它也是非常不安全的 这里和那里

答案3

一种不使用 eval 的方法,但它也非常不安全。

#!/bin/bash

f() {
  echo "command: $@"
  bash -c "$@"
}

f echo a-one a-two
f 'echo b-one b-two'
f 'echo c-one c-two > c.tmp'

相关内容