无法更改用于zsh提示符的函数中的全局变量

3

我正在尝试构建一个基于时间间隔返回输出的zsh函数。最初,“你口渴了”的条件为真,但是通过命令行更改变量thirsty并将其设置为false后,初始if语句执行,但其中的变量thirsty并没有更改全局变量thirsty有没有一种方法可以修改全局变量thirsty

thirsty=
last_time=

drink_water() {
echo -n "$thirsty"

  if [[ $thirsty == false ]]; then
    last_time="$[$(date +%s) + 10]"
    thirsty=true
    echo -n "${last_time} $(date +%s) ${thirsty}"

  elif [[ $[last_time] -lt $(date +%s) ]]; then
    echo -n " You're thirsty"
  fi

}

(顺便说一句,echo -n 实际上是不好的形式 -- POSIX 规范中的 echo-n 提供的行为标记为实现定义,这意味着不同的 shell 可以对 echo -n 做出不同的处理。如果您想要确保打印您的确切字符串而没有换行符,请使用 printf '%s' "string";还请参阅前面提到的链接中的 APPLICATION USAGE 部分。) - Charles Duffy
我有另一个函数spaceship_prompt调用了这个函数。最后,我有PROMPT ='$(spaceship_prompt)' - Lakshay Kalbhor
last_time控制着这个函数发生的事情,但是这个函数必须在其他地方的某种循环中才能被测试多次...那么那段代码在哪里呢?此外,你有没有注意到thirsty从未在任何地方被设置为false? - grail
@grail 这段代码是 zsh 主题的一部分。最终提示符将设置为从提供的函数 PROMPT='$(drink_water)' 显示。由于 thirsty 是一个全局变量,因此可以通过终端将其设置为 false。 - Lakshay Kalbhor
@LakshayKalbhor,这是理解您问题的关键信息,应该在首次提问时包含在问题中。 - Charles Duffy
显示剩余8条评论
2个回答

4

由于您的代码实际上是从以下调用的:

PROMPT='$(drink_water)'

所有内容都是作为此命令替换操作的一部分在生成的子进程中运行的($() 是“命令替换”,它会创建一个新的子进程,在该子进程中运行给定的代码,并读取子进程的输出)。当该子进程退出时,即使是全局变量也会丢失在子进程内部所做的变量更改。

如果您将更新代码直接放在 precmd 函数中,则会在打印每个提示之前运行该代码,但不会进行命令替换。即:

precmd() {
  local curr_time=$(date +%s) # this is slow, don't repeat it!
  if [[ $thirsty = false ]]; then
    last_time="$(( curr_time + 10 ))"
    thirsty=true
    PROMPT="$last_time $curr_time $thirsty"
  elif (( last_time < curr_time )); then
    PROMPT=" You're thirsty"
  fi
}

当然,你可以使用命令替换设置PROMPT,但是如果要使变量状态更新持久化,必须在该命令替换之外单独执行更新变量状态的操作。

这看起来运行良好,但是假设我有一些其他函数产生了不同于“PROMPT”的输出,那么我应该如何设置“PROMPT”为所有这些函数的输出呢?由于该函数明确地向“PROMPT”添加一个输出。 - Lakshay Kalbhor
那,坦率地说,超出了这个问题的范围。 - Charles Duffy
话虽如此,我建议将逻辑从视图中拆分为单独的函数。也就是说,在一个函数中进行更新,然后从另一个函数中生成要显示的字符串,并在命令替换中调用该函数。然而,这个建议是基于“无论价值多少,不接受任何跟进”的基础上提供的——任何试图将其变成变色龙问题的努力都会受到反对。 - Charles Duffy
哦,无论如何,你的回答解除了我的困惑,因为我一直认为问题与范围有关。谢谢。 - Lakshay Kalbhor

0

你可以使用Zsh Hooks

钩子避免了命令替换的问题,因为它们在同一个 shell 中运行,而不是在子 shell 中运行。

drink_water_prompt=

thirsty=
last_time=
drink_water_gen_prompt() {
    drink_water_prompt="$thirsty"
    if [[ $thirsty == false ]]; then
        last_time="$[$(date +%s) + 10]"
        thirsty=true
        drink_water_prompt+="${last_time} $(date +%s) ${thirsty}"
    elif [[ $[last_time] -lt $(date +%s) ]]; then
        drink_water_prompt+=" You're thirsty"
    fi
}

autoload -Uz add-zsh-hook 
add-zsh-hook precmd drink_water_gen_prompt

PROMPT='${drink_water_prompt}'

这些还允许多个precmd()函数。


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