我正在与库贝克特尔和 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
这里会发生什么?为什么输出中单词in
and之间有两个空格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()
检索它们(或将它们映射到特殊的关联数组,如ENVIRON
ofawk
或%ENV
perl ),但这并不意味着 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_VAR
shell 变量:
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
,您可以传递两个-e
和expression
参数或一个-eexpression
参数,所以在这里,您仍然可以将其-n
及其mynamespace
参数存储在标量变量中:
option=-nmynamespace
kubectl get pods $option
(请注意, if$option
是空的,它的扩展,因为它没有被引用,不会导致任何参数被传递给kubectl
。虽然这(以及空数组元素被丢弃的事实)可以被认为是一个错误特征,但在这里它恰好是你想要什么)。