【问题标题】:Repetitive directories in PATHPATH 中的重复目录
【发布时间】:2021-01-08 12:41:51
【问题描述】:

我刚刚将我的 mac (2015) 重置为 Catalina 10.15.6 并按照 this 教程通过 brew 安装了 python3。我让它工作得很好,在 ~/ 中创建了一个 .zshrc 文件,并将 brew 的位置添加到 PATH 中。昨晚,我试图在 .zshrc 文件中修改我的 PS1 变量(其中也有一行:export PATH='/usr/local/opt/python/libexec/bin:{PATH}。在使用 nano .zshrcsource .zshrc 来回工作大约 20 分钟后,我回显了我的路径,我得到了这个:/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr/sbin:/sbin:/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr/sbin:/sbin:/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr/sbin:/sbin:/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr/sbin:/sbin:/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr/sbin:/sbin:/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr/sbin:/sbin(你明白了)。

我认为这是因为我在一个会话中运行了很多次source .zshrc?每次我想在我的 .zshrc 文件中工作时,谁能解释/指导我不要遇到这个问题。

提前致谢!

【问题讨论】:

  • 我能够手动将 PATH 修复回 /usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin/bin:/usr /sbin:/sbin。我只是希望不必再这样做;)
  • 它对您有任何伤害吗?
  • export PATH='/usr/local/opt/python/libexec/bin:{PATH} 有一个无与伦比的报价。是不是和.zshrc文件里的一模一样?

标签: python macos path zsh zshrc


【解决方案1】:

在zsh中,可以设置路径只允许唯一值:

typeset -U path PATH

-U 选项可消除 path 数组和 PATH 变量中的重复项。 -U 选项适用于所有常规数组,以及使用typeset -T 设置的“绑定参数”,例如PATH

演示:

typeset +U path PATH      # +U allows duplicates
PATH=/usr/sbin
echo 1 $PATH
#=>  1 /usr/sbin

PATH=$PATH:/usr/sbin
echo 2 $PATH
#=>  2 /usr/sbin:/usr/sbin

path+=(/usr/sbin /bin)
echo 3 $PATH
#=>  3 /usr/sbin:/usr/sbin:/usr/sbin:/bin


typeset -U path PATH      # no duplicates
echo 4 $PATH
#=>  4 /usr/sbin:/bin
echo 5 $path
#=>  5 /usr/sbin /bin

PATH=$PATH:/usr/sbin:/bin
echo 6 $PATH
#=>  6 /usr/sbin:/bin

path+=(/usr/sbin /bin)
echo 7 $path
#=>  7 /usr/sbin /bin

path+=(/usr/local/sbin /usr/local/bin)
print -l $path
#=>  /usr/sbin
#=>  /bin
#=>  /usr/local/sbin
#=>  /usr/local/bin

【讨论】:

    【解决方案2】:

    基于 Tripleee 发布的答案,但使用 zsh 可以轻松提出更可靠的解决方案,该解决方案可以捕获路径中任何位置的目录,我会这样做

    for dir_to_add in .... # List of directories to be added to the PATH, unless they are there already
    do
      if (( ${path[(Ie)$dir_to_add]} ))
      then
        : # Directory already in PATH
      else
        PATH=$PATH:$dir_to_add # or: PATH=$dir_to_add:$PATH
      fi
    done
    

    请注意,标量 PATH 和数组 path 会自动保持同步。

    解释:${A[(Ie)X}是数组A中X的索引,如果X不在A中则为0。

    【讨论】:

      【解决方案3】:

      .zshrc 也会在您运行一个新的交互式子shell 时再次处理。您通常通过某些控制变量来保护变量(例如 PATH)的重复扩展。我的解决方案是这样的:

      _SH_ONCE_VERSION=0 
      : S{SH_ONCE:=} # Just in case you run with set -u enabled.
      export SH_ONCE # It is important to make this one an environment variable
      if [[ ${SH_ONCE:=0} != $_SH_ONCE_VERSION ]]
      then
        # Do modifications here, which should not be done over and over
        PATH=/what/ever/you/want:$PATH  
        export some_var=$(some_long_running_command) # should not be done on every subshell
        ...
        # Guard against repeated inclusion:
        SH_ONCE=$_SH_ONCE_VERSION
      fi
      

      从这里开始,当你第一次打开一个新的 shell 时,修改已经完成,但在每个子 shell 上,它们都会被跳过。

      现在,如果您在工作期间对 .zshrc 的这个“受保护部分”进行了一些更改,会发生什么?您肯定希望看到您的更改至少应用一次,即使冒着 PATH 变长的风险。这就是 _SH_ONCE_VERSION 发挥作用的地方。只需将其增加 1:

          _SH_ONCE_VERSION=1
      

      并且新版本被再次处理。

      您应该考虑的事项:使用这种策略,并且对于 PATH,如果在 PATH 前面而不是末尾添加新目录,它显然是有效的。

      【讨论】:

      • 这是一种非常干净的方法来保护我遇到的问题。我会试试看它是否有效。
      【解决方案4】:

      通常的解决方法是在添加之前进行检查。这适用于 POSIX sh,但我想它也应该在 Zsh 中工作。

      for d in /sbin /usr/sbin /usr/bin/bin /usr/local/bin /usr/local/opt/python/libexec/bin
      do
          case :$PATH: in
           *:"$d":*) continue ;;
          esac
          PATH=$d${PATH+":$PATH"}
      done
      

      事实上,当您启动计算机时,除了最后一个之外的所有内容都可能已经在您的PATH 上,作为系统范围环境配置的一部分。

      【讨论】:

      • 哈哈其实他们是! (虽然会完全倒退)谢谢!
      • 这完全是故意从头到尾遍历目录列表。
      • @triplee :在 zsh 中,我可能会解析数组 $path,这样可以轻松捕获要搜索的目录的任何位置。
      猜你喜欢
      • 2021-04-25
      • 1970-01-01
      • 1970-01-01
      • 2015-06-18
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 2014-01-09
      • 2012-10-10
      相关资源
      最近更新 更多