我嵌套在文件树的深处,我想找到哪个父目录包含一个文件。
例如,我在一组嵌套的Git存储库中,想要找到控制我当前所在文件的.git目录。我希望能找到诸如-searchup -iname“ .git”之类的东西
我嵌套在文件树的深处,我想找到哪个父目录包含一个文件。
例如,我在一组嵌套的Git存储库中,想要找到控制我当前所在文件的.git目录。我希望能找到诸如-searchup -iname“ .git”之类的东西
Answers:
允许使用find
选项的更通用的版本:
#!/bin/bash
set -e
path="$1"
shift 1
while [[ $path != / ]];
do
find "$path" -maxdepth 1 -mindepth 1 "$@"
# Note: if you want to ignore symlinks, use "$(realpath -s "$path"/..)"
path="$(readlink -f "$path"/..)"
done
例如(假设脚本另存为find_up.sh
)
find_up.sh some_dir -iname "foo*bar" -execdir pwd \;
...将打印所有some_dir
祖先的名称(包括其祖先),直到/
找到带有模式的文件为止。
readlink -f
如注释中所述,在使用上述脚本时,将遵循符号链接。您可以使用realpath -s
替代,如果你想通过名字来遵循的路径向上(“/富/栏”将上升到“富”,即使“酒吧”是一个符号链接) -但是,需要安装realpath
哪些默认是不安装大多数平台。
realpath -s
用来获取所需的行为。
readlink
不是该标准的一部分。只能使用POSIX shell功能来实现可移植脚本。
find
or readlink
。我建议将其更改为类似的名称,$curpath
以免影响shell变量。
git rev-parse --show-toplevel
如果您位于其中,它将打印出当前存储库的顶层目录。
其他相关选项:
# `pwd` is inside a git-controlled repository
git rev-parse --is-inside-work-tree
# `pwd` is inside the .git directory
git rev-parse --is-inside-git-dir
# path to the .git directory (may be relative or absolute)
git rev-parse --git-dir
# inverses of each other:
# `pwd` relative to root of repository
git rev-parse --show-prefix
# root of repository relative to `pwd`
git rev-parse --show-cdup
吉尔斯答案的广义版本,用于查找匹配项的第一个参数:
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
继续使用符号链接。
查找无法做到。我想不到有什么比Shell循环更简单的了。(未经测试,假设没有/.git
)
git_root=$(pwd -P 2>/dev/null || command pwd)
while [ ! -e "$git_root/.git" ]; do
git_root=${git_root%/*}
if [ "$git_root" = "" ]; then break; fi
done
对于git存储库的特定情况,您可以让git为您完成工作。
git_root=$(GIT_EDITOR=echo git config -e)
git_root=${git_root%/*}
如果使用启用了扩展的globing的zsh,则可以使用oneliner来实现:
(../)#.git(:h) # relative path to containing directory, eg. '../../..', '.'
(../)#.git(:a) # absolute path to actual file, eg. '/home/you/src/prj1/.git'
(../)#.git(:a:h) # absolute path to containing directory, eg. '/home/you/src/prj1'
说明(引自man zshexpn
):
递归球
形式
(foo/)#
的路径名组件与由零个或多个与模式foo匹配的目录组成的路径匹配。作为简写,**/
等效于(*/)#
。修饰符
在可选的单词指示符之后,您可以添加一个或多个以下修饰符的序列,每个修饰符前均带有':'。除非另有说明,否则这些修饰符还可以处理文件名生成和参数扩展的结果。
- 一种
- 将文件名转换为绝对路径:如有必要,在当前目录前加前缀,并解决对“ ..”和“。”的任何使用
- 一种
- 作为“ a ”,还尽可能解决使用符号链接的问题。请注意,“ ..”的解析发生在符号链接的解析之前。除非您的系统具有
realpath
系统调用(现代系统具有),否则此调用等效于a 。- H
- 删除尾随的路径名组件,保留头部。类似于“
dirname
”。
学分:Faux
上#zsh
为使用的最初建议(../)#.git(:h)
。
(../)
正在做什么……哦,谁是Faux on #zsh
什么?
(../)
单独使用@alexgray 并不意味着什么,但是(../)#
意味着尝试将括号内的模式扩展0次或多次,并且仅使用文件系统上实际存在的模式。因为我们正在从0扩展到n个父目录,所以我们有效地向上搜索到文件系统的根目录(请注意:您可能想在模式之后添加一些内容以使其有意义,但为了启发执行print -l (../)#
)。并且#zsh
是IRC频道,Faux
是用户名。 Faux
建议的解决方案,因此功劳。
(../)#.git(/Y1:a:h)
停在第一个找到的一个(具有最新版本的zsh)
setopt extended_glob
此版本的查找支持“查找”语法,例如@sinelaw的答案,但也支持符号链接,而无需realpath。它还支持可选的“停止于”功能,因此可以正常工作:findup .:~ -name foo
...搜索foo而不传递主目录。
#!/bin/bash
set -e
# get optional root dir
IFS=":" && arg=($1)
shift 1
path=${arg[0]}
root=${arg[1]}
[[ $root ]] || root=/
# resolve home dir
eval root=$root
# use "cd" to prevent symlinks from resolving
cd $path
while [[ "$cur" != "$root" && "$cur" != "/" ]];
do
cur="$(pwd)"
find "$cur/" -maxdepth 1 -mindepth 1 "$@"
cd ..
done
我发现使用符号链接会杀死其他一些选项。特别是git的具体答案。我已经从这个非常有效的原始答案中创建了自己的最爱。
#!/usr/bin/env bash
# usage: upsearch .git
function upsearch () {
origdir=${2-`pwd`}
test / == "$PWD" && cd "$origdir" && return || \
test -e "$1" && echo "$PWD" && cd "$origdir" && return || \
cd .. && upsearch "$1" "$origdir"
}
我在go项目中使用了符号链接,因为go希望将源代码放在某个位置,并且我希望将项目保留在〜/ projects下。我在$ GOPATH / src中创建项目,并将它们符号链接到〜/ projects。因此,运行将git rev-parse --show-toplevel
打印$ GOPATH目录,而不是〜/ projects目录。该解决方案解决了该问题。
我意识到这是一个非常具体的情况,但是我认为该解决方案很有价值。
我将sinelaw的解决方案修改为POSIX。它不遵循符号链接,包括使用do while循环搜索根目录。
find-up:
#!/usr/bin/env sh
set -e # exit on error
# Use cd and pwd to not follow symlinks.
# Unlike find, default to current directory if no arguments given.
directory="$(cd "${1:-.}"; pwd)"
shift 1 # shift off the first argument, so only options remain
while :; do
# POSIX, but gets "Permission denied" on private directories.
# Use || true to allow errors without exiting on error.
find "$directory" -path "${directory%/}/*/*" -prune -o ! -path "$directory" "$@" -print || true
# Equivalent, but -mindepth and -maxdepth aren't POSIX.
# find "$directory" -mindepth 1 -maxdepth 1 "$@"
if [ "$directory" = '/' ]; then
break # End do while loop
else
directory=$(dirname "$directory")
fi
done