java执行shell脚本_Eval Echo:一种解析 Shell 脚本传入字串中参数的方法

在 Shell 脚本编程中,我们经常会依赖 exec 命令来执行参数化的命令,例如

$ exec 'java' '-version'
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode)

但是,用户传递参数化命令的时候,未必总是传递解析后的命令,而是再次使用参数,例如

$ exec '$JAVA_HOME/bin/java' '-version'
zsh: no such file or directory: $JAVA_HOME/bin/java

可以看到,在这种情况下,我们的方法就失效了。因为 exec 命令忠实地解释它所看到的命令,而不会擅自去解析这里的 $JAVA_HOME 参数。

为了处理这个问题,我们先快速看到最终的解决方案。我们编写一个 main.sh 的辅助脚本如下。

#!/usr/bin/env bash

CMD=("$@")

for ((i=0; i<$#; i++))
do
  CMD[i]=$(eval echo ${CMD[i]})
done

exec "${CMD[@]}"

随后,即可在命令行中调用以下命令,观察到 $JAVA_HOME 参数被解析。

$ ./main.sh '$JAVA_HOME/bin/java' '-version'
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode)

现在我们来讲解如何想到 $(eval echo ${CMD[i]}) 这样的解法。

我们的问题是,参数输入是一个字符串,我们需要解析其中的变量。当 $JAVA_HOME 作为参数传入时,它并不参与 Shell 脚本解析命令的过程,而是作为一个普通的字符串被传递。因此,总的想法是我们需要一轮额外的命令解析过程,而要引入这样一个过程最自然的就是再次复用 Shell 的能力。

我们知道,将文本字符串作业 Shell 脚本执行,可以使用 eval 命令来达成。同时,这里我们只是想展开参数,而不是直接运行它们。因此我们不能直接写 $(eval ${CMD[i]})(这会导致命令原地执行),而是再引入 echo 命令来打印我们想要的命令。在 $(eval ...) 的环境中,我们再次执行了一个脚本,即我们拼接出来的 echo ${CMD[i]}。在被 exec 执行之前,在当前 Shell 中,我们将 ${CMD[i]} 先解析为 $JAVA_HOME/bin/java-version,而在 $(eval ...) 中,它们分别被 eval 进一步解释成实际的 java 路径和不需要解析的 -version,这样,我们就做到了再次解析参数的目的。

这里我们实现的方法能够再次解析 Shell 标准中规定的所有解析动作,包括花括号解析、字符串解析、通配符解析等等。实际脚本编程中,还有一种常见的解析需求,即变量名的二次解析。换句话说,定义一个值为变量名的变量,例如

REF=F

然后通过某种方式从 REF 中取到 F 的值。这在需要动态的获取变量名的情景下会非常有用。除了用刚才的 Eval Echo 方案以外,Shell 还为此提供了专门的语法,两种方法的对比如下。

RESOLVE_F=$(eval echo $$REF)
echo $RESOLVE_F # 42

RESOLVE_F=${!REF}
echo $RESOLVE_F # 42

这种技术本质上是在运行时查找符号表。在 Perl 语言中,可以简单地通过两次访问符号表来达成类似的效果。

$F = 42;
$REF = 'F';
# or $REF = F;

print $$REF; # 42

而在 Java 等语言中,可能就需要一定的反射手段才能达到类似的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值