Lif*_*ign 2 git directory bash shell
注意:我非常努力地确保这不是重复的,并且没有问过这个问题bash。据我所知,它不是重复的。
因为我对这样做的所有方式都感兴趣,并且由于git我的 OP 中强调这是一个回购,所以我不会要求答案仅使用 SHELL BUILTIN 命令,但会优先考虑仅使用的答案shell 内置函数。
我在问题中包含的脚本是我使用纯 shell 脚本回答问题的最佳尝试。
我有一个目录结构复杂的大型 bash 项目。让我们bash先把我为什么选择的问题放在一边,而不是像python.
我git用于对项目进行版本控制,该项目应该能够在用户空间的任何位置分发和安装并正常运行。因此,我有以下要求:
我希望能够将各种脚本移动到不同的目录中,而不会破坏或改变它们的运行方式。在这种情况下,可以修改脚本的执行方式(例如,通过使用其他脚本中正确的更新路径),但是在将脚本移动到新目录后,不必重写脚本的内部结构,以确保它继续工作。
我希望每个脚本都source来自vars.sh项目根目录中的一个文件中的重要变量,这样如果我决定更改项目的目录结构,我就不必在每个脚本中编辑这些变量。因此,每个脚本都需要知道项目根目录在哪里。
此类变量的示例:包含项目内目录config绝对路径或目录绝对路径等的变量tmp)。这将是确定要重新写vars.sh,如果目录结构被改变,当然,本身。但我们的想法是只需要重写vars.sh(如果脚本被移动,则适当地调用脚本)。
git,“项目根”可以定义为git的项目树根,即有目录的.git目录。据我所知,要求2任何脚本source变量和/或函数本身都需要回答这篇文章的主要问题。根据我的经验,source使用相对路径名的命令,例如
source "../../vars.sh"
Run Code Online (Sandbox Code Playgroud)
产生一个错误“没有这样的文件或目录”中bash,并侵犯了我的假设2和要求1。
请注意,DIR通过查找脚本目录的绝对路径创建的变量,使用
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
Run Code Online (Sandbox Code Playgroud)
如果放置在vars.sh项目根目录中,将根据源脚本所在的位置进行不同的评估。因此,它很有用,但我们仍然需要找到项目根目录的静态绝对路径。
我的解决方案的想法是在每个脚本中包含一个函数,该函数while使用一系列cd命令(类似于DIR上面的扩展方式)执行循环,以在项目中逐步向上导航,直到test存在目录.git提供循环中断,此时包含.git目录的目录被定义为项目根并分配给一个变量。该函数将小心避免评估用户主目录中子目录之外的任何目录:
#!/bin/bash
# a script that implements a single function to determine the root directory of
# a project. The root directory is assumed to be the one containing a .git
# directory, and is required to be in a subdirectory of $HOME.
# get the absolute path of the directory containing this script
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
find_root () {
# find the project's root directory, based on git the existence of a .git
# directory
local current_dir
current_dir=$DIR
test_dir=".git"
# make sure we are somewhere in a subdirectory of the user's home directory
# if we aren't, make a suggestion and exit
if [[ ! "$current_dir" =~ ^${HOME}/.+ ]]; then
echo "$(basename $0): Detected that we are not within a subdirectory of
the home directory ${HOME}. Please install the project containing this
script somewhere safer, in ${HOME}. Exiting" >&2
exit 1
fi
# otherwise, continue with the directory examination
while true; do
# if the test directory is found, assign the root directory and stop
# checking directories by breaking out of the loop
if [ -d "${current_dir}/${test_dir}" ]; then
root_dir="${current_dir}"
echo "Found the project root: ${root_dir}"
break
# otherwise, continue cd'ing upwards as long as the parent directory
# isn't the users's home directory, at which point we stop checking and
# exit the script with an error
else
parent_dir="$(dirname "$current_dir")"
if [[ "${parent_dir}" == "${HOME}" ]]; then
echo "Couldn't find a directory ${test_dir} in a subdirectory
of ${HOME} in order to confirm the root directory of ${0}.
Exiting" >&2
exit 1
# if we can cd to the parent directory, continue the search there
# remember to set the current directory to the parent directory;
# simply cd'ing there doesn't have any effect, since this loops
# evaluates the parameter current_dir, not $(pwd)
elif cd $parent_dir; then
echo "Examining $parent_dir for $test_dir"
current_dir=$parent_dir
continue
else
echo "Couldn't cd to the parent directory. Exiting." >&2
exit 1
fi
fi
done
# declare ROOT_DIR read only to prevent any subsequent assignment to it,
# and export it for child processes
echo "Setting ROOT_DIR to ${root_dir} as read only, and exporting ROOT_DIR"
declare -r ROOT_DIR="$root_dir"
export ROOT_DIR
return
}
#execute the function
find_root
Run Code Online (Sandbox Code Playgroud)
我选择不“回答您自己的问题”,因为我想知道您对以下方面的看法:
请注意,在我上面的示例中,.git对定义项目根目录的目录进行测试的选择是任意的。它可以很容易地是任何文件,例如.root_file,具有自定义权限或属性的文件,只要它可以由test.
任何 Git 存储库的工作树的绝对路径都可以通过git rev-parse --show-toplevel. 这也将是一个规范路径:它将解析所有符号链接,而在 Windows 上,它将以其他方式规范(例如, resolve SUBST)。即使.git目录位于其他地方(例如,使用GIT_DIR)、在子模块中(其中.git可能是文件)或在工作树中,它也可以工作。
如果您知道您的项目将始终在存储库中,那么您可以简单地执行以下操作:
. "$(git rev-parse --show-toplevel)/vars.sh"
Run Code Online (Sandbox Code Playgroud)