如何使用环境变量在 zsh 中存储标志?

如何使用环境变量在 zsh 中存储标志?

我正在与库贝克特尔和 zsh,我想将命名空间标志存储在环境变量中。

而不是写:

kubectl get pods -n mynamespace

我想做这样的事情:

n='-n mynamespace'
kubectl get pods $n

但这样做,我得到:

No resources found in  mynamespace namespace.

请注意单词in和之间的两个空格mynamespace

如果我运行kubectl get pods -n mynamespace,我会获得正确的输出:

NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          16m

这里会发生什么?为什么输出中单词inand之间有两个空格mynamespace?如何将命名空间标志存储在环境变量中?

答案1

zsh 不会对变量执行分词,而是将其值作为单个参数传递。我这里没有 kubectl,但你可以用任何东西重现该行为,例如 mkdir:

var='-p a/b/c/d'
comp% mkdir $var
mkdir: invalid option -- ' '
Try 'mkdir --help' for more information.

您可以使用以下命令调用相同的错误:

comp% mkdir '-p a/b/'
mkdir: invalid option -- ' '
Try 'mkdir --help' for more information.

您可以使用以下命令查看 zsh 真正在做什么strace -f mkdir $var

execve("/usr/bin/mkdir", ["mkdir", "-p a/b/c/d"], 0x7ffce5e6afb0 /* 61 vars */) = 0

按照建议在这个答案中 您需要启用分词,例如:

comp% mkdir -p ${=var}

在你的情况下,它将是:

kubectl get pods ${=n}

答案2

不要将 shell 变量(或任何语言的变量)与环境多变的。环境变量是语法中的字符串数组var=value,在执行命令时与参数数组一起传递给命令。与命令行参数一样,它们用于将字符串从一个命令传递到另一个不相关的命令。

Shell 具有这样的特殊性:它们将一些标量变量映射到启动时收到的一些环境变量(这些变量的名称是有效的 shell 变量名称,并且不会与特殊的 shell 变量冲突),而其他语言则使用诸如来getenv()检索它们(或将它们映射到特殊的关联数组,如ENVIRONofawk%ENVperl ),但这并不意味着 shell 不能拥有自己的变量,也不限于标量变量。

如果你想在一个变量中存储多个值,你可以使用数组/列表变量。 zsh 从 1991 年的 2.0 版本开始就支持数组(早在 bash 之前),它实际上是 shell 引入了array=(foo bar)声明它们的语法(类似于set array=(foo bar)70 年代末的 csh),现在也被 ksh93、bash、mksh 和 yash 支持。

在 中zsh,你会这样做:

options=(-n mynamespace)
kubectl get pods $options

在 中时yash,您会执行以下操作:

options=(-n mynamespace)
kubectl get pods "$options"

而在 ksh93/bash 中(zsh 也支持,它还具有不丢弃空元素的优点,并且yash):

options=(-n mynamespace)
kubectl get pods "${options[@]}"

现在,数组不能导出为环境变量,它们只是非 NUL 字节的普通字符串。

如果你必须通过这些kubectl作为的论据环境变量,您需要以某种方式使用某种形式的编码将它们组合起来,以从字符串数组中获取字符串,然后将其拆分以获取这两个参数kubectl

您选择用空格将它们连接起来作为粗略的编码。请注意,这意味着每个元素都不能包含空格。

要使用任意分隔符连接单词,在 中zsh,您可以使用j参数扩展标志,并拆分s参数扩展标志:

words=(-n mynamespace) # two arguments in an array variable
export ENV_VAR=${(j[ ])mynamespace} # joined into an environment variable

然后从该环境中调用的另一个zsh环境中调用,其中ENV_VAR环境变量已映射到$ENV_VARshell 变量:

words=(${(s[ ])ENV_VAR}) # split back into an array on space

在没有参数扩展标志的 bash 或 ksh93 中,您可以使用第一个字符(恰好是默认值中的空格字符)"${array[*]}"连接数组元素,并使用 split+glob 进行拆分:$IFS

words=(-n mynamespace) # two arguments in an array variable
IFS=' '
export ENV_VAR="${words[*]}" # joined into an environment variable

在另一个bash/ksh93那个环境中:

IFS=' '
set -o noglob
words=($ENV_VAR) # leaving a variable unquoted is split+glob in bash/ksh93!
                 # here with glob disabled above.

您也可以在 中执行此操作zsh,但只能在 sh 或 ksh 模拟模式下进行。默认情况下,zsh 不会在未加引号的参数扩展上执行 split+glob,您必须显式地请求每个参数:$=var用于拆分、$~var用于通配。因此,如果出于某种原因您想使用 IFS 分割而不是j/s参数扩展标志,您可以这样做:

IFS=' '
words=($=ENV_VAR) # explicitly request IFS-splitting, here on space only.
                  # no need to disable globbing globally like in ksh93/bash/yash
                  # as globbing is not done implicitly by default upon
                  # parameter expansion in zsh.

现在,我不知道kubectl,但对于以标准方式解析选项的应用程序(如标准getopt()C 函数),对于带有参数的选项(如 with )grep -e expression,您可以传递两个-eexpression参数或一个-eexpression参数,所以在这里,您仍然可以将其-n及其mynamespace参数存储在标量变量中:

option=-nmynamespace
kubectl get pods $option

(请注意, if$option是空的,它的扩展,因为它没有被引用,不会导致任何参数被传递给kubectl。虽然这(以及空数组元素被丢弃的事实)可以被认为是一个错误特征,但在这里它恰好是你想要什么)。

相关内容