![编写 shell 脚本以通过运行脚本来撤消对系统所做的更改](https://linux22.com/image/207848/%E7%BC%96%E5%86%99%20shell%20%E8%84%9A%E6%9C%AC%E4%BB%A5%E9%80%9A%E8%BF%87%E8%BF%90%E8%A1%8C%E8%84%9A%E6%9C%AC%E6%9D%A5%E6%92%A4%E6%B6%88%E5%AF%B9%E7%B3%BB%E7%BB%9F%E6%89%80%E5%81%9A%E7%9A%84%E6%9B%B4%E6%94%B9.png)
问题:
我是否应该编写一个脚本来撤消通过运行脚本所做的更改?
如果不是,那么我如何清理/撤消对系统所做的更改,以便我可以在运行脚本之前的系统状态下再次运行脚本(进行更改)?
如果是,那么编写好的撤消脚本应该采用什么方法?
#!bin/bash
repos_dir="$HOME/repos"
if [ ! -d "$repos_dir" ]; then
mkdir -p "$repos_dir"
fi
git clone https://github.com/bashonregardless/dotfiles.git "$repos_dir/dotfiles"
software_dir="$HOME/software"
# Create a directory to download nvim software.
if test ! -d "$software_dir"; then
mkdir "$software_dir"
fi
(
cd "$software_dir"
# Download Neovim on Linux
if test $? = 0 && "$(command -v curl)" &> dev/null; then
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim.appimage
chmod u+x nvim.appimage
else
echo "Error: curl is not installed"
echo
exit 64
fi
)
# Create "$HOME/bin" dir if it does not exist
if test ! -d "$HOME/bin"
then
mkdir "$HOME/bin"
fi
# Create a symbolic link in "$HOME/bin" dir to "$HOME/software/nvim"
ln "$software_dir/nvim.appimage" "$HOME/bin/nvim"
# Update vimrc at "$HOME/.vim/vimrc"
[ ! -d "$HOME/.vim" ] && mkdir "$HOME/.vim"
if [ ! -e "$HOME/.vim/vimrc" ]; then
# create a new file by copying dotfile vimrc
cp "$repos_dir/dotfiles/vimrc.template" "$HOME/.vim/vimrc"
else
cat "$repos_dir/dotfiles/vimrc.template" >> "$HOME/.vim/vimrc"
fi
# Update init.vim at "$HOME/.config/nvim/" (VIMCONFIG="$HOME/.config/nvim")
[ ! -d "$HOME/.config/nvim" ] && mkdir -p "$HOME/.config/nvim"
if [ ! -e "$HOME/.config/nvim/init.vim" ]; then
# create a new file by copying dotfile vimrc
cp "$repos_dir/dotfiles/init.vim.template" "$HOME/.config/nvim/init.vim"
else
cat "$repos_dir/dotfiles/init.vim.template" >> "$HOME/.config/nvim/init.vim"
fi
# add minpac vim plugin manager (See Modern vim craft)
[ ! -d "$HOME/.config/nvim/pack/minpac/opt" ] && mkdir -p "$HOME/.config/nvim/pack/minpac/opt"
cd "$HOME/.config/nvim/pack/minpac/opt"
git clone https://github.com/k-takata/minpac.git
cd -
# Update dotfiles (create, if non-existent)
if [ ! -e "$HOME/.bash_profile" ]; then
# Copy and rename in the same time
cp "$repos_dir/dotfiles/bash_profile" "$HOME/.bash_profile"
else
cat "$repos_dir/dotfiles/bash_profile" >> "$HOME/.bash_profile"
fi
if [ ! -e "$HOME/.bashrc" ]; then
cp "$repos_dir/dotfiles/bashrc" "$HOME/.bashrc"
else
cat "$repos_dir/dotfiles/bashrc" >> "$HOME/.bashrc"
fi
该脚本设置 vim 开发环境。我编写了脚本并认为正确设置就足够了,所以我第一次运行它并且它起作用了。然后我对脚本做了一些更改,并想测试它是否正确运行。这次运行时,我遇到了一些警告/错误/日志,例如:
ln: 无法创建硬链接 '/home/user/bin/nvim': 文件存在
致命:目标路径“/home/user/repos/dotfiles”已存在,并且不是空目录。
我用过几个cat file >> another-file
,那个another-file
每次运行脚本时都会将重复的内容附加到。我绝对想撤消这一点。
通过运行脚本对系统进行的更改:
- 创建 ~/repos 目录。
- 在存储库中克隆 dotfiles 存储库。
- 在存储库中克隆 compSc 存储库。
- 在家里创建软件目录。
- 在软件目录中下载nvim。
- 更改下载文件 nvim.appimage 的权限。
- 在家里创建 bin 目录。
- 使用 ~/bin/nvim 创建 nvim.appimage 的符号链接
- 创建 ~/.vim/vimrc (mkdir -p)。
- 复制/创建或附加到 ~/.vim/vimrc。
- mkdir -p "$HOME/.config/nvim/pack/minpac/opt"
- 克隆 k-takata/minpac indside "$HOME/.config/nvim/pack/minpac/opt"
- 复制/创建或附加到“$HOME/.bash_profile”
这些是在获取 ~/.bash_profile 文件之前我的系统发生的更改。 Sourcing 对我的系统进行了进一步的更改,例如更新 PATH 和其他环境变量等。
我也想撤消这些更改。
我如何复制运行脚本之前的系统状态(出于测试目的)?
我用多次通过为每一个小变化启动一个 Ubuntu VM。运行脚本后,我删除了该虚拟机并启动了一个新虚拟机。这为我提供了一台处于可以首次运行脚本状态的机器。我对每一个小变化都会这样做。很快就会变得乏味。
答案1
一般来说,您无法可靠地撤消脚本的效果。例如,脚本开头的代码片段:
if [ ! -d "$repos_dir" ]; then
mkdir -p "$repos_dir"
fi
无法可靠地撤消,因为事后您无法确定之前已经存在的目录路径有多少"$repos_dir"
。
如果您希望脚本完全可撤消,那么您需要在触摸之前记录您触摸的所有内容的原始状态,以便您可以在需要时恢复它。这就是包管理器尝试做的事情,并取得了不同程度的成功。
另一种方法是让你的脚本幂等的。这意味着运行两次与运行一次的效果完全相同。这是 Ansible 或 Puppet 等期望状态配置管理工具的方法。
上面的代码片段是幂等代码的典型示例:如果目录"$repos_dir"
在上次运行中已经存在,那么它什么也不做。
代码示例是不是幂等将是您每次运行脚本时cat file >> another-file
都会附加重复内容的情况。another-file
使其幂等需要检查 的内容file
(或者可能是其不同版本)是否已存在于 中another-file
,然后要么首先删除它,要么不再添加它。或者,您可以保留another-file
不带内容的基本版本file
,并使用它来连接,如下所示:
if [ ! -e another-file.base ]; then
mv another-file another-file.base
fi
cat another-file.base file > another-file