美文网首页
Mac 系统终端美化与 ZSH 多设备配置同步共享

Mac 系统终端美化与 ZSH 多设备配置同步共享

作者: 越前君 | 来源:发表于2022-07-24 00:37 被阅读0次
    配图源自 Freepik

    由于个人对 iTerm2 等第三方终端工具不太感冒,因此一直在用系统内置终端。相比那些花里胡哨的东西,系统自带的「Terminal」可谓是简陋啊,哈哈~

    今天,终于有空折腾一下它了。

    👆 此前的样子。

    一、为什么要使用 zsh?

    在 Unix-like 操作系统中供选择的 Shell 解析器种类有很多,最常见的是 bash 和 zsh。

    相较之下,zsh 主要有以下优点:

    • 完全兼容 bash,因此 !# /bin/bash 这类脚本也是完美支持的。
    • 更强大的 Tab 补全。
    • 更智能的目录切换。
    • 命令选项补全。
    • 文件、目录大小写自动更正(最最最直观的感受,太方便了)。
    • 丰富的主题、插件(Oh My Zsh)
    • 可查看命令输入历史记录。

    总的来说,Tab 大法好,有事没事 Tab 一下,可以帮你省掉很多命令的输入。

    自 macOS 10.15 起,系统使用 zsh 作为默认 Shell。如果你还在用 bash,可参考「在 Mac 上将 zsh 用作默认 Shell」一文进行迁移。

    二、安装 Oh My Zsh

    zsh 的功能很多,但是配置较为繁琐,详细配置请看 Archlinux Zsh

    但是,Robby Russell 开发的 Oh My Zsh 项目极大地改变了配置繁琐的现象,而且带来了强大的插件和主题功能。

    安装

    $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
    

    安装完成后,它被存放于 ~/.oh-my-zsh 目录,在里面我们可以找到它提供的任何插件、主题等。

    需要注意的是,此前的 ~/.zshrc 配置文件会被重命名为 ~/.zshrc.pre-oh-my-zsh。若有需要,可将此前的配置重新添加到 ~/.zshrc 里面。

    启用插件

    Oh My Zsh 提供的插件很多很多很多,详见 Plugins Wiki。如需启用某插件,只需在 ~/.zshrc 引入即可。多个插件之间使用「空白符」隔开,但不能用逗号 , 隔开,否则会被中断。

    举个例子,以下将会按顺序启用 nvm、git、ruby 插件。

    plugins=(nvm git ruby)
    

    切换主题

    Oh My Zsh 提供了超过 150 种主题,默认主题是 robbyrussell,详见 Themes Wiki。同样地,如需配置主题,则添加到 ~/.zshrc 里面。

    ZSH_THEME="robbyrussell"
    

    它还提供了一些花里胡哨的配置,比如随机主题。

    # 每打开一个新的 Shell 窗口,随机选择内置的主题。
    ZSH_THEME="random"
    
    # 或者从多个候选主题中随机展示。
    ZSH_THEME_RANDOM_CANDIDATES=(
      "robbyrussell"
      "agnoster"
    )
    
    # 甚至可以排除掉不喜欢的主题,这样就不会在随机主题中出现。
    ZSH_THEME_RANDOM_IGNORED=(pygmalion tjkirch_mod)
    

    我大致看了下内置的主题,似乎没有能够满足我的强迫症的,因此后面会自定义。

    三、主题 DIY

    一番折腾之后的效果:

    设置窗体、字体等颜色

    系统内置的终端工具默认主题「Basic」是白底黑字的。虽然也内置了几种主题供选择,但架不住它丑啊。由于个人不太喜欢深色模式,因此 Basic 这个主题用着还不错。

    于是,就自定义了一个新主题「Dark Themer」,并提交到 GitHub 👉 toFrankie/terminal-theme

    其实命令输出中花里胡哨的颜色,都是读取 ANSI 颜色的配置而已,如果你想要自定义,自行修改即可。

    也可以新增、删除、导入或导出主题,点击下方左侧对应图标即可。

    自定义命令提示符

    我这里使用了 Oh My Zsh 的默认主题 robbyrussell,其命令提示符为:

    PROMPT="%(?:%{$fg_bold[green]%}➜ :%{$fg_bold[red]%}➜ )"
    PROMPT+=' %{$fg[cyan]%}%c%{$reset_color%} $(git_prompt_info)'
    

    个人不太喜欢,于是改成了这样:

    PROMPT="%(?:%{$fg_bold[green]%}→ ●:%{$fg_bold[red]%}→ ●)%{$fg_bold[white]%}●"
    PROMPT+=" %{$fg_bold[yellow]%}%c %{$reset_color%}$ "
    RPROMPT="[%{$fg[white]%}%*%{$reset_color%}]"
    

    前后对比效果如下:

    配置 vim

    如果 vim 模式下仍然是「白底」,可以在 ~/.vimrc 中添加以下配置:

    set background=dark "设置背景色
    

    这样的话,主题就统一了。

    四、配置同步

    由于我这边有 MacBook Pro 和 iMac 两个设备,那么设备间同步就显得很重要了,可以省去修改其一设备配置,需要拷贝到另一设备以同步更新的麻烦。

    首先,我们的 zsh 配置都是用户级别的,也就是存放在 ~ 目录下:

    ~/.zshrc
    

    如何做配置同步共享呢?

    思路非常简单:

    1. 将配置文件存放于 iCloud 云盘,可多设备同步。
    2. 在本地配置中引入 iCloud 云盘同步的配置文件。

    首先,我们在 iCloud 云盘创建一个目录,比如 Terminal

    $ cd ~/Library/Mobile\ Documents/com~apple~CloudDocs
    $ mkdir Terminal
    

    然后,将 ~/.zshrc 拷贝至 Terminal 目录,并重命名为 zshrc.sh

    $ cp ~/.zshrc ~/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc.sh
    

    以上创建目录、拷贝、重命名等操作可直接在 Finder 中完成。

    接着,修改本地的 ~/.zshrc,可以移除掉里面原先的配置,然后写入一行配置即可:source $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc.sh,可直接使用以下命令完成:

    $ echo 'source $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc.sh' > ~/.zshrc
    

    所以 ~/.zshrc 配置文件就变成了:

    source $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc.sh
    

    source 命令用于执行一个脚本,通常用于重新加载一个配置文件。

    最后,刷新下 ~/.zshrc 配置文件。

    $ source ~/.zshrc
    

    在其他设备下,只要修改一次本地配置文件,并刷新配置即可。

    配置拆分

    前面做配置同步的初衷是解决多设备的场景,另一方面是可以做备份,下次换机器可省去配置的麻烦。

    既然是多设备,那极有可能各设备的环境是不一样的,也就是配置会有所不同。基于此场景,直接采用上述方式显然是不妥的。假设设备 A 安装了 nvm,然后在 iCloud 云盘 Terminal/zshrc.sh 里添加了对应的配置,而设备 B 是没有安装 nvm 的,但由于 B 共享了配置,在 B 设备中执行 souce ~/.zshrc 就会导致出错。

    因此,我们要对 Terminal/zshrc.sh 配置进行拆分:

    ~/Library/Mobile Documents/com~apple~CloudDocs/Terminal/
    ├── zshrc.sh            # 统一引入以下细分配置
    ├── zshrc-common.sh     # 各设备共同配置,比如 Oh-My-Zsh 的一些配置等
    ├── zshrc-imac.sh       # iMac 设备特有配置
    └── zshrc-mbp.sh        # MacBook Pro 设备特有配置 
    

    👆 以上按自个实际情况拆分。这里文件名不以 . 开头的原因是不想隐藏文件,毕竟它不存在于本地用户的主目录下,没必要隐藏了。另外,扩展名是可省略的,甚至可随意自定义。由于 ~/.zshrc 本身就是一个 Shell 脚本,而 Shell 脚本扩展名是可选的。但按习惯,多将其定义为 .sh,加之主流编辑器对此扩展名一般都有语法高亮。

    按设备引入对应配置

    首先,我们现在 Terminal/zshrc.sh 引入 zshrc-common.sh 共同的配置文件。

    # zshrc.sh
    
    # 加载共同配置
    source $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc-common.sh
    
    # 加载设备特有配置
    # 而且应放在共同配置后面加载,原则上它们的优先级应该较高的。
    # ...
    

    接着,要解决按设备引入各自特有的配置文件。用以下几种方法来区分设备:

    $ hostname
    Frankies-iMac.local
    
    $ scutil --get LocalHostName
    Frankies-iMac
    
    $ scutil --get ComputerName
    Frankie's iMac
    

    对应如下:

    其中 LocalHostNameComputerName 格式化之后的名称,比如空格会替换为连字符 -,一些特殊符号也会被忽略。hostname 则是在 LocalHostName 加上 .local 的名称。但由于 hostname 读取顺序的问题(详见),它可能会受到所连接网络的影响,因此它可能不是固定值。

    基于以上考虑,这里使用 scutil --get ComputerName 命令,它的返回值取决于你输入的电脑名称。但注意 ComputerName 为 macOS 独有。

    接着,编写一个 Shell 函数,按设备电脑名称加载对应配置。

    # 根据设备电脑名称,读取对应配置
    function load_device_zsh_configuration() {
      local computer_name=$(scutil --get ComputerName)
      local icloud_config_dir="$HOME/Library/Mobile Documents/com~apple~CloudDocs/Terminal"
      if [[ $computer_name =~ 'MacBook' ]]; then
        local config_file="$icloud_config_dir/zshrc-mbp.sh"
        if [ -f "$config_file" ]; then
          source "$config_file"
        fi
      elif [[ $computer_name =~ 'iMac' ]]; then
        local config_file="$icloud_config_dir/zshrc-imac.sh"
        if [ -f "$config_file" ]; then
          source "$config_file"
        fi
      fi
    }
    load_device_zsh_configuration
    unset -f load_device_zsh_configuration
    

    👆 以上几个地方按需调整。一是 icloud_config_dir 后面的目录,也就是 iCloud 云盘中存放配置文件的目录。二是类似 $computer_name =~ 'MacBook' 这行,意思是当 ComputerName 中包含 MacBook 字符串时,执行 if 语句,将加载 zshrc-mbp.sh 配置。

    因此,zshrc.sh 的配置如下:

    # Load zsh common configuration
    source $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc-common.sh
    
    # Load device zsh configuration
    function load_device_zsh_configuration() {
      local computer_name=$(scutil --get ComputerName)
      local icloud_config_dir="$HOME/Library/Mobile Documents/com~apple~CloudDocs/Terminal"
      if [[ $computer_name =~ 'MacBook' ]]; then
        local config_file="$icloud_config_dir/zshrc-mbp.sh"
        if [ -f "$config_file" ]; then
          source "$config_file"
        fi
      elif [[ $computer_name =~ 'iMac' ]]; then
        local config_file="$icloud_config_dir/zshrc-imac.sh"
        if [ -f "$config_file" ]; then
          source "$config_file"
        fi
      fi
    }
    load_device_zsh_configuration
    unset -f load_device_zsh_configuration
    

    一次失败的尝试与思考

    最初,我有一种利用链接文件特性的思路:

    Terminal/zshrc.sh~/.zshrc 建立硬链接关系,此时两者就是真正意义上的同一文件(inode 号码相同),修改其中一方,另一方会同步修改。而且删除任意一方,对另一方不会产生影响。

    但是,由于 iCloud 云盘中的文件会进行同步操作。据我观察,当文件同步完成之后,尽管文件名不会发生改变,但 inode 号码会发生变化的。这样的话,会导致前面建立的硬链接关系破裂,即 Terminal/zshrc.sh~/.zshrc 的 inode 不再相同,修改任意一方,另一方都不会同步修改。因此,基于硬链接的思路是不行的。

    然后,我又萌生了另一种思路:利用软链接文件。软链接文件的文件内容存放的是文件路径,前面 iCloud 同步不会影响文件名,因此看起来似乎是可行的:

    # 创建 Terminal/zshrc.sh 的软链接至 ~/.zshrc.icloud
    $ ln -s ~/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc.sh ~/.zshrc.icloud
    
    # 在 ~/.zshrc 写入配置
    $ echo 'source ~/.zshrc.icloud' > ~/.zshrc
    
    # 刷新配置
    $ souce ~/.zshrc
    

    👆 以上在 ~/.zshrc 加载 ~/.zshrc.icloud 的时候,由于后者是 Terminal/zshrc.sh 的软链接,命令执行时会找到 Terminal/zshrc.sh 的内容进行加载。

    这种方案看着是可行的,但属实有点多次一举了。何不直接在 ~/.zshrc 加载 Terminal/zshrc.sh 呢?

    若对硬链接与软链接文件特效不太了解的话,可看 Linux 链接文件详解

    其他

    完成以上多设备同步、配置共享之后,后续配置调整,应根据实际情况对以下文件作出修改,而不是直接修改 ~/.zshrc 本地配置。

    ~/Library/Mobile Documents/com~apple~CloudDocs/Terminal/
    ├── zshrc-common.sh
    ├── zshrc-imac.sh
    └── zshrc-mbp.sh
    

    由于 iCloud 云盘的 Terminal 目录不好记,还长,加之容易忽略 Mobile\ Documents 空格的问题。因此,我们可以在 zshrc-common.sh 设置一些变量或别名以快速定位到配置文件。以下仅供参考:

    # zshrc-common.sh
    
    # 刷新本地配置
    alias sourcez="source ~/.zshrc"
    
    # 设置别名,可快速打开各配置文件
    alias openz-common="open $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc-common.sh"
    alias openz-imac="open $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc-imac.sh"
    alias openz-mbp="open $HOME/Library/Mobile\ Documents/com~apple~CloudDocs/Terminal/zshrc-mbp.sh"
    
    # 设置变量,可直接通过 `cd $ICLOUD_TERMINAL_DIR` 命令打开目录
    export ICLOUD_TERMINAL_DIR="$HOME/Library/Mobile Documents/com~apple~CloudDocs/Terminal"
    

    这样的话,比如要往 zshrc-imac.sh 中添加配置,可以这样:

    # 打开 zshrc-imac.sh 配置文件
    $ openz-imac
    
    # 往 zshrc-imac.sh 配置文件中添加一行配置:export CUR_DEVICE="Frankie's iMac"
    
    $ 刷新配置
    # soucez
    
    # 测试一下
    $ echo $CUR_DEVICE
    Frankie's iMac
    

    甚至,可以给每个配置文件设置一个变量 Configuration_File_Name,然后利用类似 echo 'some configuration' >> $Configuration_File_Name 形式往配置文件中追加配置。请自行发挥。

    The end.

    相关文章

      网友评论

          本文标题:Mac 系统终端美化与 ZSH 多设备配置同步共享

          本文链接:https://www.haomeiwen.com/subject/obibirtx.html