我有一组shell命令,如下所示
if check-some-condition $a then;
do stuff
run-exit-code $a
fi
其中check-some-condition
和run-exit-code
可以用只带一个参数$a的函数来代替,而do stuff
可能是几个shell命令的占位符。
(my-macro $a stuff)
用上面的代码替换它吗?我用的是Bash,但是我可以用任何其他的shell,如果他们有使这更容易的特性的话。我一开始想用函数,但是我不认为我可以传入一个命令块。
2条答案
按热度按时间e3bfsja21#
在shell中没有宏定义系统,因此shell语法不能被用来扩展宏。但是,shell有一个文本的
eval
命令。你可以写一个函数来合成shell语法,比如通过将给定的参数插入到模板中。该函数可以打印该语法。调用者可以使用$(...)
命令替换语法来捕获该命令并将其传递给eval
:每次执行该代码行时都会发生扩展。
我在很少的情况下做过类似的事情,我不记得所有的细节,但我记得代码也利用了Bash
local
变量,它有动态作用域,就像古老的Lisp方言和Common Lisp中的defvar
变量。在Bash中,
eval
发生在一个动态环境中,它可以看到周围的local
变量,这是可以利用的;它可以帮助产生一些类似于宏的语义。在具有词法作用域的局部变量的Lisp中,eval
-ed代码不能访问这些变量,但宏替代的代码可以。在动态作用域下,evaled
代码可以访问和赋值周围的局部变量。这里有一个例子,注意,由于
eval
是一个命令,它不能控制其参数空间中发生的扩展,因为它被调用了(类似于Lisp中的eval
是一个不控制参数求值的函数),客户端代码被引用的责任所拖累。我们可以让它
没有引号的作品,代价是在
dofor
内部有更多的神秘逃避。假设我们用内置命令
evalcmd
扩展了shell,这样我们就可以编写如下代码而不是上面的代码:我们能把它写成一个shell函数吗?结果是,是的:
现在,虽然仍然是可怕的效率,它大大更符合人体工程学。
最后,让我们问:我们是否可以将
dofor
拆分为一个生成代码的dofor_impl
和一个调用dofor_impl
并调用evcmd
语义的dofor
命令?同样,可以:这对于一些简单的用法来说是不错的,但是我们不能实现的是不必将
$i
放在引号中,这样在调用dofor
之前就不会发生替换。6fe3ivhb2#
在Bash中,您可以定义函数,如下所示:
并且您的代码可以执行与变量关联的命令:
所以你可以写,例如:
如果您希望
stuff
引用$var
,则需要添加eval
:这样就可以编写一个带引号的bash表达式,并在函数的上下文中对其进行求值: