发布于 2026-01-06 0 阅读
0

~/.dotfiles 为什么、什么、如何构建 dotfiles DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

~/.dotfiles

为什么

什么

如何

结构

点文件

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

为什么

假设你收到了一台全新的电脑。你需要安装很多软件(你最喜欢的浏览器、代码编辑器、窗口管理工具等等),还需要进行一些配置和快捷键设置。开箱后,你需要多久才能完全上手工作?一小时?一天?还是一周?

如果手动操作,肯定会出错。因为我们都是人。

一份精心编写的待办事项清单或许能帮你成功完成任务,但是等等,难道所有下载、点击和配置都是你手动完成的吗?你从事计算机科学工作,用电脑来处理重复枯燥的任务。

现在假设您的笔记本电脑已准备就绪,可以开始工作了。您对它很满意,并且希望在运行不同操作系统的台式机上也拥有相同的工作环境,那么您应该从头开始吗?

这就是为什么你需要安装自动化。它们通常被称为“点文件”。让我们深入了解一下。

什么

我们希望将各种环境的所有配置放在一个易于共享或重用的地方,并且最好是幂等的。

语境

以下指南和说明适用于类 Unix 操作系统。抱歉,微软用户,由于我有一段时间没用过这个系统了,所以无法提供相关信息。它适用于Windows 子系统 for Linux,因为它的底层也是类 Unix 系统。

内容

我们有两件事需要处理:软件的安装和配置。

  • 安装方面,值得庆幸的是,大多数操作系统都内置或主流的软件包管理器,例如 Aptitude、Brew、Pacman 等等。这是一个很好的起点,但并非所有工具或软件都适用于所有操作系统或架构。您可能需要下载二进制文件或压缩包,并将其安装到正确的位置。

  • 配置方面则更为复杂多样。有些工具依赖于配置文件,有些依赖于环境变量,还有一些则难以配置,例如隐藏文件或二进制文件。

请注意,macOS 的软件包管理器Brew不是内置的,必须先手动安装。

贮存

看来我们开始讨论代码了,特别是基础设施即代码(IaC)。如今,代码都存储在版本控制软件(VCS)中。其中应用最广泛的是Git。为了让所有人都能共享代码,你必须将 Git 仓库存储在 GitHub、GitLab、Bitbucket 等平台上。这些平台易于使用,深受社区欢迎,而且也容易找到。

Git 拥有众多特性,我们将重点介绍分支。借助分支,您可以architecture x operation-system轻松管理各种系统。例如,您可以创建一个用于“个人 macOS”的分支、一个用于“工作 Ubuntu”的分支、一个用于“家庭 Raspbian”的分支等等。您还可以根据主机名、环境变量等信息在代码中添加条件,从而执行相应的操作。

我的 Dotfiles 文件涵盖了个人和专业应用场景,以及amd64macOSarm64和 Linux 操作系统上的各种架构。它们全部位于同一个代码仓库中,只有两个分支(`<path>`main和 `<path> work`)。我针对不同操作系统之间的许多差异进行了规范化处理。

如何

巴什

你可以在 GitHub (或其他地方)找到许多工具来引导你的 Dotfiles。有些人选择依赖 Ansible,有些人则选择一些需要自行安装的工具。但是,如何安装一个能够安装这些工具的工具呢?手动安装是不可能的。这就像一个先有鸡还是先有蛋的问题。

显然,你需要一个起点,并确保它在所有地方都能访问。在类 Unix 系统中,Bash是通用的。但对于一款已有三十年历史的软件来说,承担起作为通用工具的责任并非易事。

例如,在最新的 macOS 版本中,由于许可协议变更bash,预装的版本是3.2.572007 年的版本。我不会详细解释 v3 和 v5(2021 年的最新版本)之间的所有区别,但您可以在下面看到一个差异。

MY_VARIABLE="macOS"

# Lower case, works only with bash 4+
echo "${MY_VARIABLE,,}"

# Lower case, the old way, bash 3 compliant
echo "${MY_VARIABLE}" | tr "[:upper:]" "[:lower:]"
Enter fullscreen mode Exit fullscreen mode

所以,Bash是大多数系统上已经预装的兼容性最强的软件。我们知道如何运行代码,但之后如何才能检索到它呢?

Zip & Curl

我们之前已经看到,存储 Dotfiles 的最佳方式是使用 Git,但git克隆仓库时必须先安装 Git,这又是一个先有鸡还是先有蛋的问题。幸运的是,像 GitHub 或 GitLab 这样的解决方案提供了将仓库下载为Zipgit压缩包的方法。而 Git ,例如Git ,在大多数发行版中都是原生支持的。unzipbash

为了获取数据,我们也需要一个工具。curl从终端向互联网发出请求的常用方法,而且,你猜怎么着,它也内置在大多数发行版中。

金三角

得益于Bash 的管道功能,我们可以运行curl一个引导脚本,通过管道将其传递给 `--archive` 命令,bash然后通过 `--archive` 命令加载仓库的归档文件来启动安装unzip。安装完成后,所有工具都已就绪(已在 macOS、Manjaro 和 Debian 10 上测试)。

curl "https://my.bootstrap.script" | bash
Enter fullscreen mode Exit fullscreen mode

⚠️ 无论如何,在执行此类命令时,务必仔细检查你要运行的内容,尤其是一些“单行安装脚本”需要额外配置时sudo互联网很棒,但并非每个人都像互联网上的人那样行事。

在引导脚本中,您可以选择 Dotfiles 的存放位置(我这里放在 `/etc/bootstrap` 目录下${HOME}/code/dotfiles)。请注意选项curl的详细格式。编写脚本时,您不是在终端里快速输入,而是在编写代码,这些代码将会被共享,并且必须易于其他人理解。因此,请尽可能详细地描述这些选项。

#!/usr/bin/env bash

set -o nounset -o pipefail -o errexit

main() {
  local INSTALL_PATH="${HOME}/code"
  local GITHUB_USER="ViBiOh"
  local DOTFILES_NAME="dotfiles"
  local DOTFILES_BRANCH="main"
  local ARCHIVE_FILENAME="${INSTALL_PATH}/dotfiles.zip"

  mkdir -p "${INSTALL_PATH}"

  curl \
    --disable \
    --silent \
    --show-error \
    --location \
    --max-time 60 \
    --output "${ARCHIVE_FILENAME}" "https://github.com/${GITHUB_USER}/${DOTFILES_NAME}/archive/${DOTFILES_BRANCH}.zip"
  unzip "${ARCHIVE_FILENAME}" -d "${INSTALL_PATH}"
  rm -f "${ARCHIVE_FILENAME}"

  rm -rf "${INSTALL_PATH:?}/${DOTFILES_NAME}"
  mv "${INSTALL_PATH}/${DOTFILES_NAME}-${DOTFILES_BRANCH}" "${INSTALL_PATH}/${DOTFILES_NAME}"

  (
    cd "${INSTALL_PATH}/${DOTFILES_NAME}"
    "./init" -a

    git init
    git remote add origin "http://github.com/${GITHUB_USER}/${DOTFILES_NAME}.git"
    git fetch origin
    git checkout --force "${DOTFILES_BRANCH}"
  )
}

main
Enter fullscreen mode Exit fullscreen mode

引导脚本会“首次下载”所有必需的文件。下载完成后,init脚本将接管后续操作。该init脚本负责安装和配置所有内容,您将来会多次运行它。

脚本末尾可以看到一些git命令。安装成功后,Git 将被安装,我会将下载的归档文件(不包含.git文件夹)与 Git 上游仓库进行同步。

结构

首先,我们需要在 Dotfiles 仓库中添加之前提到的两个脚本:bootstrapinit。此外,您还需要至少 3 个文件夹:installsymlinkssources。接下来我们将深入探讨它们。

> tree -L 1
.
├── bootstrap
├── init
├── install/
├── sources/
└── symlinks/
Enter fullscreen mode Exit fullscreen mode

安装

软件通常以二进制文件的形式提供,并且可能可以通过您常用的软件包管理器安装。但有些任务严格来说并不算是“软件安装”,却至少需要在您的计算机上运行一次(例如禁用操作系统中不需要的功能、生成无法创建符号链接的配置文件等等)。在我看来,这些都属于安装脚本的范畴。

我使用的软件并非全部都可通过软件包管理器获取。软件包管理器经常会安装一些我不需要的可选软件,而且这些安装是全局的,可能会影响其他用户。我倾向于直接从 GitHub 下载二进制文件并将其安装到我的系统中,${PATH}而不是依赖软件包管理器。此外,这样更容易坚持使用特定的版本,而且二进制文件的更新速度通常比软件包的上游更新速度更快。

理想情况下,Dotfiles 不应该运行任何命令sudo。你应该在机器上安装配置,并尽可能将所有内容都放在你的配置文件中${HOME}例如,我会创建一个${HOME}/opt/文件夹来存放我的东西(GOPATH、Python 包等等)。我会将 Dotfiles 添加${HOME}/opt/bin到我的配置文件中${PATH}。如果我删除了这个opt/文件夹,只会影响到我自己。

在这个install/文件夹中,你可以通过将不同的文件拆分来区分安装过程中的各个方面。这样,你就可以根据环境变量禁用某个脚本。例如,在我的服务器上,我不需要安装代码编辑器。我设置了一个环境变量DOTFILES_NO_EDITOR,安装脚本就不会尝试运行该install/editor文件。

我认为安装过程分为三个步骤:

  • clean安装必须是幂等的,可能需要在安装前进行清理,或者简单地“重置”Dotfiles安装。
  • install安装过程可以理解为:运行脚本从软件包管理器下载,将二进制文件放入相应的文件夹等等。
  • credentials从密码管理器中检索密钥并将其添加到配置文件中。有关更多详细信息,请参阅“密钥”部分sources/。此阶段必须在所有安装完成后执行(您必须先安装密码管理器)。

为了分别运行每个阶段,我们可以依赖于文件中是否存在相应的函数。在 Bash 中,执行文件和检查函数是否存在都很容易。

如果我们把之前讨论的所有内容浓缩成一个函数,它就会像下面的代码一样,放到init脚本中。

browse_install() {
  while IFS= read -r -d '' file; do
    local BASENAME_FILE
    BASENAME_FILE="$(basename "${file}")"

    local UPPERCASE_FILENAME
    UPPERCASE_FILENAME="$(printf "%s" "${BASENAME_FILE}" | tr "[:lower:]" "[:upper:]")"
    local DISABLE_VARIABLE_NAME="DOTFILES_NO_${UPPERCASE_FILENAME}"

    if [[ ${!DISABLE_VARIABLE_NAME:-} == "true" ]]; then
      continue
    fi

    if [[ -r ${file} ]]; then
      for action in "${@}"; do
        unset -f "${action}"
      done

      source "${file}"

      for action in "${@}"; do
        if [[ $(type -t "${action}") == "function" ]]; then
          printf "%s - %s" "${action}" "${BASENAME_FILE}"
          "${action}"
        fi
      done
    fi
  done < <(find "${CURRENT_DIR}/install" -type f -print0 | LC_ALL=C sort --zero-terminated)
  # ${CURRENT_DIR} is the root of the Dotfiles repository
}

browse_install clean install
browse_install credentials
Enter fullscreen mode Exit fullscreen mode

符号链接

最容易配置的工具是那些依赖于你主目录下以点号开头的单个文件的工具。这就是代码仓库名称的由来:著名的“点文件”。你肯定见过它们:`.gitignore` .bashrc、`.gitignore`.vimrc或 `.gitignore` .gitconfig。为了将该文件置于版本控制系统(Git)之下,但又不提交整个仓库${HOME},一个简单的技巧是使用符号链接。该文件存在于 ` .gitignore`${HOME}文件夹中,因此你的工具可以读取它,但其内容仍然保留在受版本控制的原始文件夹中。两全其美。

~/ > ls -la
.bashrc -> /Users/macbook/code/dotfiles/symlinks/bashrc
.curlrc -> /Users/macbook/code/dotfiles/symlinks/curlrc
.gitconfig -> /Users/macbook/code/dotfiles/symlinks/gitconfig
.ignore -> /Users/macbook/code/dotfiles/symlinks/ignore
.inputrc -> /Users/macbook/code/dotfiles/symlinks/inputrc
.tmux.conf -> /Users/macbook/code/dotfiles/symlinks/tmux.conf
.vimrc -> /Users/macbook/code/dotfiles/symlinks/vimrc
Enter fullscreen mode Exit fullscreen mode

安装很简单:把所有文件放在.file一个目录(例如符号链接)中,然后将该目录下的每个文件链接到你的${HOME}文件夹。注意,我的文件名中没有点号(例如bashrc),我在创建符号链接时才添加点号。在大多数操作系统中,点文件默认是隐藏文件。我不希望它在代码中隐藏,而只在使用时才显示。

可以使用以下代码片段创建符号链接,将其放入init脚本中。

create_symlinks() {
  while IFS= read -r -d '' file; do
    local BASENAME_FILE
    BASENAME_FILE="$(basename "${file}")"

    if [[ -n ${FILE_LIMIT} ]] && [[ ${BASENAME_FILE} != "${FILE_LIMIT}" ]]; then
      continue
    fi

    rm -f "${HOME}/.${BASENAME_FILE}"
    ln -s "${file}" "${HOME}/.${BASENAME_FILE}"
  done < <(find "${CURRENT_DIR}/symlinks" -type f -print0)
  # ${CURRENT_DIR} is the root of the Dotfiles repository
}
Enter fullscreen mode Exit fullscreen mode

关于符号链接,温馨提示:它们是动态的“内容”。当您拉取 Dotfiles 仓库的新版本时,配置会立即更改。

👍 它也有好处:你拉取代码后就什么都不用做了。

👎 它也有缺点:如果你的symlinks/gitconfig文件存在 Git 冲突,你的 Git 可能会崩溃,因为你的~/.gitconfig文件无效 😅。

永远都是先有鸡还是先有蛋的问题。

来源

配置 shell 环境有点不一样,换句话说,比较复杂!通常配置 shell 的方法是使用`/etc/shell` ~/.bashrc(或~/.zshrc`/etc/ .you-name-it-rcshell`)。我们之前就是用符号链接配置的。但是把所有工具的配置都放在同一个文件里,会让文件变得难以阅读,而且为了区分代码,还会添加很多注释。这不是一个简洁的方法。

幸运的是,正确拆分文件取决于你。我的~/.bashrc数据源是文件夹中包含的每个文件dotfiles/sources/

我不想硬编码 Dotfiles 文件夹的路径。多亏了已经链接到仓库内某个文件夹的符号链接,我才能用这个${BASH_SOURCE[0]}技巧找到文件的实际位置。

sources可以通过以下代码片段获取文件夹的来源,将其放入symlinks/.bashrc文件中。

source_all() {
  local SCRIPT_DIR
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

  for file in "${SCRIPT_DIR}/../sources/"*; do
    [[ -r ${file} ]] && [[ -f ${file} ]] && source "${file}"
  done
}
Enter fullscreen mode Exit fullscreen mode

这个文件夹里sources/存放着你需要的所有终端配置,例如环境变量、函数、别名等等。是时候好好复习一下了。

Dotfiles俱乐部第一条规则

你不会把秘密信息放在 Dotfiles 文件中。

Dotfiles俱乐部规则#2

你不应该把秘密信息放在你的.dt文件中。

如果你想在环境变量中存储密钥(例如令牌、密码),请将它们放在 `.` 文件中~/.localrc。不要创建任何符号链接!顾名思义,它始终保持“本地”状态。

if [[ -e "${HOME}/.localrc" ]]; then
  source "${HOME}/.localrc"
fi
Enter fullscreen mode Exit fullscreen mode

秘密

如果您已经阅读过Dotfiles Club的两条规则,您可能会想知道如何在不将密钥存放在存储库中的情况下自动配置计算机。密钥管理的秘诀在于:密码管理器。

因为我们使用的是 Unix 环境,所以我个人使用易于脚本化的密码管理器,它依赖于可靠的PGP 加密,并将所有信息存储在 Git 中。我需要导入我的 GPG 公钥(基于私钥生成的Yubikey),这没问题。安全是一个严肃的问题。您可以使用任何其他密码管理器,安装带有脚本的 CLI,install/这样您应该能够在配置credentials阶段检索您的凭据并将其推送到您的配置文件中。

技巧和窍门

请记住,您的配置文件需要在各种安装环境下的不同阶段都能正常运行。务必保持向后兼容性,并对错误保持宽容态度。

使用 source 命令时,不应该使用 Bash 标志-o nounset -o pipefail -o errexit,因为如果发生错误,它会导致 Bash 启动崩溃,最终你可能会被锁定在终端之外。

此外,如果您使用非内置工具,请务必使用以下代码片段检查其是否存在。您可能禁用了该工具的安装,或者它在您的系统架构上可能不可用等等。

if command -v git >/dev/null 2>&1; then
  # do `git` related stuff
fi
Enter fullscreen mode Exit fullscreen mode

Bashrc

使用.bashrc自动加载环境是件好事,但在执行无头操作(例如)时则不需要rsync。在这种情况下,它反而会降低速度。幸运的是,有一个技巧可以禁用自动加载.bashrc

[[ -z ${PS1:-} ]] && return
Enter fullscreen mode Exit fullscreen mode

macOS 在这方面比较特殊。默认情况下,它不会查找该~/.bashrc文件,而是查找~/.bash_profile. 您可以通过创建此文件并将其指向您的.bashrc.

#!/usr/bin/env bash

if [[ -f "${HOME}/.bashrc" ]]; then
  source "${HOME}/.bashrc"
fi
Enter fullscreen mode Exit fullscreen mode

命令

浏览文件必须按特定顺序进行,因为文件之间可能存在依赖关系(例如,${PATH}在检查软件是否已安装之前更改变量,或者在使用 Python 之前安装 Python pip)。在单个文件中不会出现这类问题,因为您只需将代码行放在正确的位置即可。

for使用 `bash`或 ` bash.push` 遍历文件夹时find,Bash 依赖于环境变量定义的字母顺序LC_ALL / LC_COLLATE。您可以通过确保文件按字母顺序排列来控制加载顺序。小提示:下划线位于任何小写字母之前。

为了正确配置您的区域设置,您可以在 之前添加以下代码片段source_all,该代码片段会在存在区域设置时优雅地设置它。

set_locale() {
  local LOCALES=("en_US.UTF-8" "en_US.utf8" "C.UTF-8" "C")
  local ALL_LOCALES
  ALL_LOCALES="$(locale -a)"

  for locale in "${LOCALES[@]}"; do
    if [[ $(echo "${ALL_LOCALES}" | grep --count "${locale}") -eq 1 ]]; then
      export LC_ALL="${locale}"
      export LANG="${locale}"
      export LANGUAGE="${locale}"

      return
    fi
  done

  return 1
}
Enter fullscreen mode Exit fullscreen mode

总而言之

我在这个 Gist中创建了一个最简单的 dotfiles 仓库。Gist 中无法创建文件夹,所以例如symlinks_bashrc应该是symlinks/bashrc

你会找到一段关于如何在 macOS 上安装 Brew 并更改用户默认 shell 的代码片段。如果你使用 Brew 安装 Bash,你会有两个版本bash:一个是/bin/bash3.2.57 版本,另一个是/usr/local/bin/bash5 及更高版本。

GitHub 标志 ViBiOh / dotfiles

用于配置终端环境的点文件

点文件

它是如何运作的?

请点击此处查看我的文章。

安装

curl "https://dotfiles.vibioh.fr/bootstrap.sh" | bash
Enter fullscreen mode Exit fullscreen mode

更新

"${HOME}/code/dotfiles/init.sh" -a
Enter fullscreen mode Exit fullscreen mode

配置

您可以设置以下环境变量来自定义安装行为:

  • DOTFILES_NODE="true"将执行installations/node文件安装(替换NODE为目录中任意大写文件名installations/
#!/usr/bin/env bash

# Dotfiles configuration example for a server

export DOTFILES__SCRIPTS="true"
export DOTFILES_RIPGREP="true"
export DOTFILES_TMUX="true"
export DOTFILES_VIM="true"
export DOTFILES_YQ="true"
Enter fullscreen mode Exit fullscreen mode

SSH

ssh-keygen -t ed25519 -a 100 -C "$(whoami)@$(hostname)" -f "${HOME}/.ssh/id_ed25519"
Enter fullscreen mode Exit fullscreen mode

通用石油气

gpg --full-generate-key
Enter fullscreen mode Exit fullscreen mode

命令行工具(macOS)

请运行以下命令重新安装:

sudo rm -rf $(xcode-select -print-path)
xcode-select --install
Enter fullscreen mode Exit fullscreen mode

酿造

如果出现故障,请使用以下命令进行修复。

sudo chown -R "$(whoami)" "$(brew
Enter fullscreen mode Exit fullscreen mode

我的个人 Dotfiles 代码库语法更简洁,也是我撰写本文的灵感来源。它是一个“模板仓库”,欢迎随意使用。我的 Dotfiles 代码库经历了近 2000 次提交,最终成型,这真是一段漫长的旅程,我只是想和大家分享一下。希望您阅读愉快!

PS:英语不是我的母语,这是我第一次在这里发帖,请多多包涵,我非常乐意接受改进建议 =)

封面图片来自 Unsplash

文章来源:https://dev.to/vibioh/dotfiles-5695