Bash 函数调用作为参数给出的命令

发布于 2024-09-08 20:26:50 字数 809 浏览 5 评论 0原文

如何在 bash 中编写一个函数来执行作为参数给出的命令,其中

  • 给定的命令可能是别名
  • 参数必须完全按照给定的方式传递;可以不进行评估

换句话说,如何编写尽可能透明的包装器函数。

例如,包装函数的目标可以是在给定命令之前和之后设置当前目录,和/或设置环境变量,或者计算给定命令需要多长时间,...作为一个简单的例子,我在这里采用一个函数它只打印一行,然后执行给定的命令。

第一次尝试:

function wrap1 {
   echo Starting: "$@"
   "$@"
}

您可以像 wrap1 echo hello 一样使用它。但问题是你不能执行 alias myalias echo 然后调用 wrap1 myalias hello:它不会解析别名。

使用 eval 的另一次尝试:

function wrap2 {
   echo Starting: "$@"
   eval "$@"
}

现在调用别名可以了。但问题是它也评估论点。例如,wrap2 echo "\\a" 仅打印 a 而不是 \a,因为参数被评估两次。

shopt -s Expand_aliases 似乎也没有帮助。

有没有一种方法可以像wrap2一样评估别名,但仍然像wrap1一样直接传递参数?

How do you write a function in bash that executes the command that it is given as an argument, where

  • The given command may be an alias
  • Arguments must be passed on exactly as given; no evaluating may be done

In other words, how to write an as-transparent-as-possible wrapper function.

The goal of the wrapper function could for example be to set the current directory before and after the given command, and/or set environment variables, or time how long the given command takes,... As a simple example here I take a function that just prints a line and then executes the given command.

A first attempt:

function wrap1 {
   echo Starting: "$@"
   "$@"
}

You could use it like wrap1 echo hello. But the problem is you cannot do alias myalias echo and then call wrap1 myalias hello: it wouldn't resolve the alias.

Another attempt using eval:

function wrap2 {
   echo Starting: "$@"
   eval "$@"
}

Now calling an alias works. But the problem is it evaluates the arguments too. For example wrap2 echo "\\a" prints just a instead of \a because the arguments are evaluated twice.

shopt -s expand_aliases doesn't seem to help here either.

Is there a way to both evaluate aliases like wrap2, but still pass on the arguments directly like wrap1?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

勿挽旧人 2024-09-15 20:26:50

您(呃,我)可以使用 printf %q 来转义参数。
乍一看,用 printf 转义然后执行 eval 总是给出与直接传递参数相同的结果。

wrap() {
    echo Starting: "$@"
    eval $(printf "%q " "$@")
}

You (uh, I) can use printf %q to escape the arguments.
At first sight, escaping with printf and then doing eval always gives the same result as passing the arguments directly.

wrap() {
    echo Starting: "$@"
    eval $(printf "%q " "$@")
}
一曲爱恨情仇 2024-09-15 20:26:50

似乎可以使用双eval

eval "eval x=($(alias y | cut -s -d '=' -f 2))"
# now the array x contains the split expansion of alias y
"${x[@]}" "${other_args[@]}"

所以也许你的函数可以写成如下:

wrap() {
    eval "eval prefix=($(alias $1 | cut -s -d '=' -f 2))"
    shift
    "${prefix[@]}" "$@"
}

但是,eval是邪恶的,双eval这是双重邪恶的,并且由于某种原因,别名不会在脚本中扩展。

It seems to be possible with a double eval:

eval "eval x=($(alias y | cut -s -d '=' -f 2))"
# now the array x contains the split expansion of alias y
"${x[@]}" "${other_args[@]}"

So maybe your function could be written as follows:

wrap() {
    eval "eval prefix=($(alias $1 | cut -s -d '=' -f 2))"
    shift
    "${prefix[@]}" "$@"
}

However, eval is evil, and double eval is double evil, and aliases are not expanded in scripts for a reason.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文