如何从头开始构建自己的Linux Dotfiles Manager

As a new linux 🐧 user, you might realize that there are a bunch of configuration files present in your system. These special files are called "dotfiles".

作为新的Linux用户,您可能会意识到系统中存在大量配置文件。 这些特殊文件称为“点文件”。

In this tutorial we will learn how to make a dotfiles manager and create a backup of these files on GitHub.

在本教程中,我们将学习如何制作一个dotfiles管理器以及如何在GitHub上创建这些文件的备份。

What are these .dotfiles you may ask? And why do we need them?

您可能会问这些.dotfiles是什么? 为什么我们需要它们?

Dotfiles are usually associated with specific programs installed on your system and are used to customize those programs/software.

点文件通常与系统上安装的特定程序相关联,并用于自定义那些程序/软件。

For example, if you have zsh as your default shell your will have a .zshrc file in your HOME directory. Some other examples include:

例如,如果将zsh作为默认外壳,则HOME目录中将具有.zshrc文件。 其他一些示例包括:

  1. .vimrc: this bad boi is used for configuring your VIM Editor.

    .vimrc :该错误的boi用于配置VIM编辑器。

  2. .bashrc: available by default, used for changing bash settings.

    .bashrc :默认情况下可用,用于更改bash设置。

  3. .bash_aliases: this file is generally used to store your command aliases.

    .bash_aliases :此文件通常用于存储命令别名。

  4. .gitconfig: stores configurations related to Git.

    .gitconfig :存储与Git相关的配置。

  5. .gitmessage: Used to provide a commit messsage template while using git commit.

    .gitmessage :用于在使用git commit时提供提交消息模板。

These .dotfiles change over time as you start customizing linux according to your needs.

当您开始根据需要定制Linux时,这些.dotfiles会随着时间而变化。

Creating a back-up of these files is necessary if in some case you mess up something 😬 and want to go back to a previous stable state. That's where VCS (Version Control Software) comes in.

如果在某些情况下您弄乱了某些东西并想回到以前的稳定状态,则必须创建这些文件的备份。 这就是VCS(版本控制软件)的出现。

Here, we will learn how to automate this task by writing a simple shell script and storing our dotfiles on GitHub.

在这里,我们将学习如何通过编写简单的shell脚本并将点文件存储在GitHub上来自动执行此任务。

lets do it rock
Source : giphy.com
资料来源:giphy.com

内容 (Contents)

第一步 (First Steps)

Oh before we move any further, let's name our script: dotman, (dot)file (man)ager. Do you like it 😅 ?

在进一步操作之前,让我们命名脚本: dotman ,(dot)file(man)ager。 你喜欢😅吗?

Before we write our first line of code, we need to lay out our requirements and design for how our shell script should work.

在编写第一行代码之前,我们需要对我们的shell脚本应该如何工作进行布局和设计。

我们的要求 (Our Requirements)

We are going to make dotman simple & easy to use. It should be able to:

我们将使dotman简单易用。 它应该能够:

  1. Find dotfiles present inside our system 🔍.

    查找我们系统中存在的dotfile🔍。

  2. Differentiate between files present in our git repository to those on our system.

    将git存储库中存在的文件与系统上的文件区分开。

  3. Update our dotfiles repo (either push to remote or pull from it).

    更新我们的dotfiles存储库(推送到远程或从中拉出)。

  4. Be easy to use (we don't want 5 different arguments in a single script).

    易于使用(我们不想在一个脚本中使用5个不同的参数)。

让可视化 (Lets Visualize)

dotman-flowchart

获取依赖 (Getting Dependencies)

  1. Git We need Git, because we may want to go back to a previous version of our dotfile. Plus we are going to store our dotfiles in a VCS Host (GitHub/GitLab/Bitbucket/Gittea). Don't have Git Installed? Go through the following guide to learn how to install it according to your system.

    Git我们需要Git,因为我们可能想回到点文件的先前版本。 另外,我们将把点文件存储在VCS主机中(GitHub / GitLab / Bitbucket / Gittea)。 还没有安装Git吗? 仔细阅读以下指南,了解如何根据您的系统进行安装。

  2. Bash This is going to be available on your Linux/Unix/MacOS machines by default. Verify this by checking the version bash --version. It should be something like this. Don't worry about the version too much, as our script will work fine for Bash >=3.

    Bash默认情况下,它将在您的Linux / Unix / MacOS计算机上可用。 通过检查版本bash --version验证这一点。 应该是这样的。 不用太担心版本,因为我们的脚本对于Bash> = 3可以正常工作。

GNU bash, version 4.4.20(1)-release (i686-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

开始编码 (Start Coding)

So now we have everything setup. Fire up your favorite editor/IDE.

现在,我们已完成所有设置。 启动您喜欢的编辑器/ IDE。

coder chimpanzee
Source: giphy.com
资料来源:giphy.com

We need to declare a she bang to indicate we are going to invoke an interpreter for execution. In the start of the script include this line:

我们需要声明一个爆炸,以表明我们将调用解释器以执行。 在脚本的开头包含以下行:

#!/usr/bin/env bash

The command (program) env is executed as a new process which then calls the command that was provided as an argument.

命令(程序) env作为新进程执行,然后调用作为参数提供的命令。

In our case bash is automatically started by the env process. That is its env responsibility to find where is bash on our system and substitute its path in the script. You could replace bash with, for example, python or ruby.

在我们的情况下, bash由env进程自动启动。 这就是它的env责任找到哪里是bash在我们的系统和脚本替代它的路径。 您可以将bash替换为pythonruby

Now just change file permissions to make our script executable.

现在只需更改文件许可权即可使我们的脚本可执行。

chmod +x dotman.sh

We will be using the functional style of programming in this script, that is every piece of the task is going to be inside some function(). Let's follow the flow chart we visualized above and write our first function, init_check().

我们将在此脚本中使用编程的功能样式,即任务的每一部分都将放在某个function()内部。 让我们按照上面可视化的流程图并编写我们的第一个函数init_check()

We are going to rely only on 2 inputs from the user:

我们将仅依靠来自用户的2个输入:

  1. DOT_DEST: the location of repository in your local system.

    DOT_DEST :本地系统中存储库的位置。

  2. DOT_REPO: the url to the remote dotfile repo.

    DOT_REPO :远程点文件仓库的URL。

These 2 variables must be present inside your default shell config (.bashrc for e.g). We will learn how to do this later in this tutorial.

这两个变量必须存在于默认的shell配置中(例如.bashrc )。 我们将在本教程的后面部分学习如何执行此操作。

init_check() {
	# Check wether its a first time use or not
	if [[ -z ${DOT_REPO} && -z ${DOT_DEST} ]]; then
	    # show first time setup menu
		# initial_setup
	else
		# repo_check
	    # manage
	fi
}

The -z option is used to check whether a variable is set or not (that is, if its available to our script or not). If it is not, then we are going to invoke our initial_setup() function. Otherwise we will check if the repository is cloned and is present inside the DOT_DEST folder.

-z选项用于检查是否设置了变量(即,变量是否可用于我们的脚本)。 如果不是,那么我们将调用我们的initial_setup()函数。 否则,我们将检查存储库是否已克隆并且是否存在于DOT_DEST文件夹中。

Now let's code the initial_setup function:

现在,让我们编写initial_setup函数的代码:

initial_setup() {
	echo -e "\n\nFirst time use, Set Up d○tman"
	echo -e "....................................\n"
	read -p "Enter dotfiles repository URL : " -r DOT_REPO

	read -p "Where should I clone $(basename "${DOT_REPO}") (${HOME}/..): " -r DOT_DEST
	DOT_DEST=${DOT_DEST:-$HOME}
	if [[ -d "$HOME/$DOT_DEST" ]]; then
		# clone the repo in the destination directory
		if git -C "${HOME}/${DOT_DEST}" clone "${DOT_REPO}"; then
			add_env "$DOT_REPO" "$DOT_DEST"
			echo -e "\ndotman successfully configured"
			goodbye
		else
			# invalid arguments to exit, Repository Not Found
			echo -e "\n$DOT_REPO Unavailable. Exiting"
			exit 1
		fi
	else
		echo -e "\n$DOT_DEST Not a Valid directory"
		exit 1
	fi
}

Pretty basic, right? Now, let's go through this together and understand what's happening.

很基本吧? 现在,让我们一起经历一下,了解正在发生的事情。

  • The read startement is a shell bulitin which is used to take input from the terminal. The -p option specifies a prompt before taking an input.

    read开头是一个shell bulitin,用于从终端获取输入。 -p选项指定输入之前的提示。

  • The next line after read is called a Parameter Expansion, If the user doesn't input DOT_DEST then the default value is assigned as /home/username/ (If DOT_DEST is unset or null, the expansion of $HOME is substituted) Otherwise, the value entered by user is substituted.

    读取后的下一行称为Parameter Expansion ,如果用户未输入DOT_DEST,则默认值分配为/home/username/ (如果DOT_DEST未设置或为null,则替换$ HOME的扩展名)否则,则用户输入的值将被替换。

  • The -d inside the if statement checks whether the directory exists (or technically) the directory user provided is actually a valid path in our system or not.

    if语句中的-d检查目录是否存在(或从技术上来说)用户提供的目录实际上是我们系统中的有效路径。

  • The -C option is used in git to clone the repository to a user-specified path.

    git中使用-C选项将存储库克隆到用户指定的路径。

Now let's see how to export environment variables in the function add_env().

现在让我们看看如何在add_env()函数中导出环境变量。

add_env() {
	# export environment variables
	echo -e "\nExporting env variables DOT_DEST & DOT_REPO ..."

	current_shell=$(basename "$SHELL")
	if [[ $current_shell == "zsh" ]]; then
		echo "export DOT_REPO=$1" >> "$HOME"/.zshrc
		echo "export DOT_DEST=$2" >> "$HOME"/.zshrc
	elif [[ $current_shell == "bash" ]]; then
		# assume we have a fallback to bash
		echo "export DOT_REPO=$1" >> "$HOME"/.bashrc
		echo "export DOT_DEST=$2" >> "$HOME"/.bashrc
	else
		echo "Couldn't export DOT_REPO and DOT_DEST."
		echo "Consider exporting them manually".
		exit 1
	fi
	echo -e "Configuration for SHELL: $current_shell has been updated."
}

Running echo $SHELL in your terminal will give you the path for your default shell. The basename command is used to print the "Name" of our SHELL (that is, the actual name without any leading /).

在终端中运行echo $SHELL将为您提供默认Shell的路径。 basename命令用于打印SHELL的“名称”(即,没有任何前导/的实际名称)。

> echo $SHELL
/usr/bin/zsh
> basename $SHELL
zsh
  • The export is a well-used statement: it lets you export :) environment variables.

    export是一个很好用的语句:它允许您导出:)环境变量。

  • >> is called a redirection operator, that is the output of the statement echo "export DOT_DEST=$2" is directed (appended) to the end of zshrc file.

    >>被称为重定向运算符,即语句echo“ export DOT_DEST = $ 2”的输出被定向(附加)到zshrc文件的末尾。

Now, once the user has completed the first time setup we need to show them the "manager" options.

现在,一旦用户完成了第一次设置,我们就需要向他们显示“经理”选项。

manage-menu-flowchart
manage() {
	while :
	do
		echo -e "\n[1] Show diff"
		echo -e "[2] Push changed dotfiles to remote"
		echo -e "[3] Pull latest changes from remote"
		echo -e "[4] List all dotfiles"
		echo -e "[q/Q] Quit Session"
		# Default choice is [1]
		read -p "What do you want me to do ? [1]: " -n 1 -r USER_INPUT
		# See Parameter Expansion
		USER_INPUT=${USER_INPUT:-1}
		case $USER_INPUT in
			[1]* ) show_diff_check;;
			[2]* ) dot_push;;
			[3]* ) dot_pull;;
			[4]* ) find_dotfiles;;
			[q/Q]* ) exit;;
			* )     printf "\n%s\n" "Invalid Input, Try Again";;
		esac
	done
}
  • You are already familiar with read. The -n 1 option specifies what length of input is allowed, in our case the user can only input one character amongst 1, 2, 3, 4, q and Q.

    您已经熟悉read-n 1选项指定允许输入的长度,在本例中,用户只能输入1、2、3、4,q和Q中的一个字符。

Now we have to find all dotfiles in our HOME directory.

现在我们必须在我们的HOME目录中找到所有的点文件。

find_dotfiles() {
	printf "\n"
	readarray -t dotfiles < <( find "${HOME}" -maxdepth 1 -name ".*" -type f )
	printf '%s\n' "${dotfiles[@]}"
}

The function is divided into 2 parts:

该功能分为2部分:

  1. find

    find

    The find command you guessed right, searches for files and directories in our system. Let's understand it part by part.

    您猜对的find命令在我们的系统中搜索文件和目录。 让我们对它进行部分了解。

  • The -type f options specifies that we only want to search for regular files and not directories, character or block, or device files.

    -type f选项指定我们只想搜索常规文件,而不是目录,字符或块或设备文件。

  • The -maxdepth option tells find to descend at most 1 level (a non-negative integer) levels of directories below the starting-points. You could search sub-directories by replacing 1 with 2, 3 etc.

    -maxdepth选项告诉find在-maxdepth以下最多将-maxdepth 1级(非负整数)。 您可以通过将1替换为2、3等来搜索子目录。

  • -name takes a pattern(glob) for searching. For example you can search for all .py files: -name ".py".

    -name采用一个模式(全局)进行搜索。 例如,您可以搜索所有.py文件: -name ".py"

  1. readarray (also a synonym for mapfile)

    readarray (也是mapfile的同义词)

    reads lines from the standard input into the indexed array variable

    从标准输入读取行到索引数组变量

    dotfiles.

    dotfiles

    The

    -t option removes any trailing delimiter (default newline) from each line read.

    -t选项从读取的每一行中删除任何结尾的定界符(默认换行符)。

Note: If you have an older version of Bash (<4), readarray might not be present as a builtin. We can achieve the same functionality by using a while loop instead.

注意:如果您具有较旧的Bash版本(<4),则readarray可能不作为内置readarray出现。 我们可以通过使用while循环来实现相同的功能。

while read -r value; do
    dotfiles+=($value)
done < <( find "${HOME}" -maxdepth 1 -name ".*" -type f )

We are now going to make one of the most important functions in our script, diff_check.

现在,我们将在脚本中diff_check最重要的功能之一diff_check

diff_check() {

	if [[ -z $1 ]]; then
		declare -ag file_arr
	fi

	# dotfiles in repository
	readarray -t dotfiles_repo < <( find "${HOME}/${DOT_DEST}/$(basename "${DOT_REPO}")" -maxdepth 1 -name ".*" -type f )

	# check length here ?
	for (( i=0; i<"${#dotfiles_repo[@]}"; i++))
	do
		dotfile_name=$(basename "${dotfiles_repo[$i]}")
		# compare the HOME version of dotfile to that of repo
		diff=$(diff -u --suppress-common-lines --color=always "${dotfiles_repo[$i]}" "${HOME}/${dotfile_name}")
		if [[ $diff != "" ]]; then
			if [[ $1 == "show" ]]; then
				printf "\n\n%s" "Running diff between ${HOME}/${dotfile_name} and "
				printf "%s\n" "${dotfiles_repo[$i]}"
				printf "%s\n\n" "$diff"
			fi
			file_arr+=("${dotfile_name}")
		fi
	done
	if [[ ${#file_arr} == 0 ]]; then
		echo -e "\n\nNo Changes in dotfiles."
		return
	fi
}

show_diff_check() {
	diff_check "show"
}

Our goal here is to find the dotfiles already present in the repository and compare them with the one available in our HOME directory.

我们的目标是找到存储库中已经存在的点文件,并将其与我们HOME目录中可用的点文件进行比较。

  • The declare keyword lets us create variables. The -a option is used to create arrays and -g tells declare to make the variables available "globally" inside the script.

    使用declare关键字可以创建变量。 -a选项用于创建数组,而-g告诉声明使变量在脚本内“全局”可用。

  • ${#file_arr} gives us the length of the array.

    ${#file_arr}给出了数组的长度。

The next important command is diff which is used to compare files line-by-line. For example:

下一个重要的命令是diff ,它用于逐行比较文件。 例如:

> echo -e "abc\ndef\nghi" >> fileA.txt
> echo -e "abc\nlmn\nghi" >> fileB.txt
> cat fileA.txt
abc
def
ghi
> cat fileB.txt
abc
lmn
ghi
> diff -u fileA.txt fileB.txt
--- fileA.txt	2020-07-17 16:24:16.138172662 +0530
+++ fileB.txt	2020-07-17 16:24:26.686075270 +0530
@@ -1,3 +1,3 @@
 abc
-def
+lmn
 ghi

The dot_push() function.

dot_push()函数。

dot_push() {
	diff_check
	echo -e "\nFollowing dotfiles changed : "
	for file in "${file_arr[@]}"; do
		echo "$file"
		cp "${HOME}/$file" "${HOME}/${DOT_DEST}/$(basename "${DOT_REPO}")"
	done

	dot_repo="${HOME}/${DOT_DEST}/$(basename "${DOT_REPO}")"
	git -C "$dot_repo" add -A
	
	echo -e "Enter Commit Message (Ctrl + d to save):"
	commit=$(</dev/stdin)

	git -C "$dot_repo" commit -m "$commit"
	
	# Run Git Push
	git -C "$dot_repo" push
}

We are overwriting files here by copying them to our dotfile repo using the cp command.

我们通过使用cp命令将文件复制到我们的dotfile存储库中来覆盖文件。

And finally the dot_pull() function:

最后是dot_pull()函数:

dot_pull() {
	# pull changes (if any) from the host repo
	echo -e "\nPulling dotfiles ..."
	dot_repo="${HOME}/${DOT_DEST}/$(basename "${DOT_REPO}")"
	echo -e "\nPulling changes in $dot_repo\n"
	git -C "$dot_repo" pull origin master
}

爵士💅🏼我们的剧本 (Jazzing 💅🏼 up our script)

Up until now we have achieved what we initially visualized. But you know what, something's missing ....... 🤔

到目前为止,我们已经实现了最初的可视化效果。 但是你知道吗,缺少什么.............

Colors

色彩

Colorful cat rainbow waves
Source: tenor.com
资料来源:tenor.com

There are a lot of ways to do that, but the popular one is using escape sequences. But we are going to use a tool called tput which is a human friendly interface to output colors according to the user's terminal. It is available by default in Linux/MacOS. Here is a short demo.

有很多方法可以做到这一点,但是流行的是使用转义序列 。 但是我们将使用称为tput的工具,该工具是人性化的界面,可以根据用户的终端输出颜色。 在Linux / MacOS中默认情况下可用。 这是一个简短的演示。

To print text in bold

粗体打印文本

echo "$(tput bold)This$(tput sgr0) word is bold"

To change background color.

更改背景颜色。

echo "$(tput setab 10)This text has green background$(tput sgr0)"

To change foreground color

更改前景色

echo "$(tput setaf 10)This text has blue color$(tput sgr0)"

You can also combine attributes.

您还可以组合属性。

echo "$(tput smul)$(tput setaf 10) This text is underlined & green $(tput rmul)$(tput sgr0)"

Let me leave this task with you: add your favorite colors in the script. Read this guide to learn and explore more about tput.

让我把这个任务留给您:在脚本中添加您喜欢的颜色。 阅读指南可了解和探索有关tput的更多信息。

最终结果 (The End Result)

I hope you are still with me at the point. But it's the end :( and we have a nice looking dotfile manager now.

希望您现在还和我在一起。 到此为止:(和,我们现在有了一个漂亮的dotfile管理器。

Happy and excited kermit
Source: giphy.com
资料来源:giphy.com

Now just run the script (if you haven't already) to see it in action.

现在只需运行脚本(如果尚未运行)即可查看其运行情况。

./dotman.sh

You can see my version of dotman if you need a reference. Feel free to create any issues if you have any questions about this tutorial or email them to me directly.

如果需要参考,可以查看我的dotman版本。 如果您对本教程有任何疑问,请随时提出任何问题,或直接将其发送给我。

Bhupesh-V/dotman - GitHub

I have made it available as a template so you can use it to hack your own version of dotman.

我已经将其作为模板提供,因此您可以使用它来破解自己的dotman版本。

摘要 (Summary)

Let's summarize some important things we learned in this tutorial.

让我们总结一下我们在本教程中学到的一些重要知识。

  1. Use basename /path/to/dir/file/ to get the filename from a path.

    使用basename /path/to/dir/file/从路径获取文件名。

  2. Use git -C /path/to/clone/to clone https://repo.url to clone the repository to a different directory from the current working directory.

    使用git -C /path/to/clone/to clone https://repo.url将存储git -C /path/to/clone/to clone https://repo.url到与当前工作目录不同的目录。

  3. echo $SHELL can be used to determine what is your default shell.

    echo $SHELL可用于确定默认外壳程序是什么。

  4. Use find to search for files and folders in your Linux system.

    使用find在Linux系统中搜索文件和文件夹。

  5. The diff command is used to compare 2 files. Similar to git diff.

    diff命令用于比较2个文件。 类似于git diff

  6. Arrays declared inside a function are only accessible inside that function. Use the -g option to make them global, for example declare -ag file_arr.

    在函数内部声明的数组只能在该函数内部访问。 使用-g选项使它们成为全局declare -ag file_arr ,例如, declare -ag file_arr

  7. tput can be used to display colorized text on terminal.

    tput可用于在终端上显示彩色文本。

If you liked this tutorial, you can read more of my stuff at my blog. You can also connect with me on Twitter.

如果您喜欢本教程,则可以在Blog上阅读更多内容。 您也可以在Twitter上与我联系。

Happy Learning 🖖

快乐学习🖖

翻译自: https://www.freecodecamp.org/news/build-your-own-dotfiles-manager-from-scratch/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值