为什么在Z shell脚本中执行相同的命令与直接在Z shell中运行时解释不同?

4
我写了一个辅助脚本(Z shell(可执行zsh)方言),用于安装Python的Poetry软件包管理器。一个奇怪的问题出现了,当在(a)shell脚本中运行时,相同的shell命令被解释为不同的方式,与(b)直接在Z shell中运行时不同。脚本的摘录如下:
#!/bin/zsh
set -x
INSTALL_SPECIFIC_VERSION="--version 1.6.1"
# Why does this not work?
echo "curl -sSL URL | python3 - $INSTALL_SPECIFIC_VERSION"
# will print:
# curl -sSL URL | python3 - --version 1.6.1
curl -sSL URL | python3 - "$INSTALL_SPECIFIC_VERSION"
# The fix I use which works but I don't understand why, is:
# curl -sSL URL | python3 - $(printf "%s" "$INSTALL_SPECIFIC_VERSION")
set +x

当执行shell脚本时,以下是结果:
  /usr/bin/zsh test_script.zsh
+test_script.zsh:3> INSTALL_SPECIFIC_VERSION='--version 1.6.1'
+test_script.zsh:5> echo 'curl -sSL URL | python3 - --version 1.6.1'
curl -sSL URL | python3 - --version 1.6.1
+test_script.zsh:8> curl -sSL URL
+test_script.zsh:8> python3 - '--version 1.6.1'
usage: - [-h] [-p] [--version VERSION] [-f] [-y] [--uninstall] [--path PATH] [--git GIT]
-: error: unrecognized arguments: --version 1.6.1
+test_script.zsh:9> set +x

参数--version 1.6.1无法被远程调用的shell脚本解释。
当我在Z shell中调用打印(echo)的命令时,它可以正常工作。请参考:
➜  /usr/bin/zsh -c "curl -sSL URL | python3 - --version 1.6.1"
Retrieving Poetry metadata

The latest version (1.6.1) is already installed.

我的当前修复在shell文件中是这样的:
# The fix I use which works but I don't understand why, is:
curl -sSL URL | python3 - $(printf "%s" "$INSTALL_SPECIFIC_VERSION")

为什么变量的值被传递/解释得不同呢?我在这里漏掉了什么吗?
Z shell 版本:5.8.1 (x86_64-ubuntu-linux-gnu)

2
因为python3 - "$var"只接收到2个参数(-$var的值),而python3 - --version 1.6.1是使用3个参数(---version1.6.1)运行python。你的脚本和你的交互调用是不等价的:等价的交互调用是python3 - '--version 1.6.1' - undefined
1
你的“修复”(其实并不是修复)是一种复杂的写法,等同于 python3 - $INSTALL_SPECIFIC_VERSION - undefined
你的建议确实有效,当我从变量定义中删除引号时:INSTALL_SPECIFIC_VERSION=--version 1.6.1。如果你是指这个问题,我会这样回答。请随时添加其他答案。 - undefined
3
INSTALL_SPECIFIC_VERSION=--version 1.6.1 是无意义的,你应该会得到一个错误 _zsh: command not found: 1.6.1_。一般来说,如果你写类似 A=B C 的语句,zsh 会尝试在一个环境中运行程序 C,其中变量 A 的值被设置为 B - undefined
1
相关链接:在zsh中变量扩展与bash中不同 - undefined
显示剩余4条评论
1个回答

2
你不能(明智地或理智地)将多个单词(参数、注释等)存储在一个变量中。然而,你可以通过扩展带有替代值的参数来包含一个单词:

${parameter:+word} 使用替代值。如果参数未设置或为空,将替换为null;否则,将替换为单词的扩展。

在你的情况下:

#!/bin/sh -x
# you don't need zsh for this, plain sh works just fine
INSTALL_SPECIFIC_VERSION="1.6.1"
curl -sSL URL | python3 - ${INSTALL_SPECIFIC_VERSION:+--version "$INSTALL_SPECIFIC_VERSION"}

如果参数未设置为一个值,那么这将扩展为nothing,如果参数包含一个值,那么它将扩展为two个单词--version1.6.1
或者,可以使用简单的if语句。
#!/bin/sh -x
INSTALL_SPECIFIC_VERSION="1.6.1"
if test "$INSTALL_SPECIFIC_VERSION"; then
  curl -sSL URL | python3 - --version "$INSTALL_SPECIFIC_VERSION"
else
  curl -sSL URL | python3 -
fi

这就是为什么你总是想要引用你的参数扩展的原因:在bash/POSIX shell中忘记引用变量的安全隐患。从远程服务器运行任意命令也是安全关键的,如果可能的话应该避免(但有时候确实无法避免)。
资源:

据我所知,zsh中的参数展开过程中可以使用一个标志来强制进行单词拆分。不过,为了避免这种情况,最好采用这里展示的方法。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接