私はファイルツリーの奥深くにネストされており、どの親ディレクトリにファイルが含まれているかを知りたいのですが。
たとえば、ネストされたGitリポジトリのセットにいて、現在のファイルを制御する.gitディレクトリを見つけたいと思っています。find -searchup -iname ".git"のようなものを期待しています
私はファイルツリーの奥深くにネストされており、どの親ディレクトリにファイルが含まれているかを知りたいのですが。
たとえば、ネストされたGitリポジトリのセットにいて、現在のファイルを制御する.gitディレクトリを見つけたいと思っています。find -searchup -iname ".git"のようなものを期待しています
回答:
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
代わりに、名前でパスをフォローしたい場合は使用できます(「/ foo / bar」は「bar」がシンボリックリンクであっても「foo」になります)-ただし、realpath
デフォルトではインストールされないインストールが必要ですほとんどのプラットフォーム。
realpath -s
して、必要な動作を取得できます。
readlink
標準の一部ではありません。移植可能なスクリプトは、POSIXシェル機能のみで実装できます。
find
またはを見つけられないため、zshでは機能しませんreadlink
。$curpath
シェル変数をシャドウしないように、代わりに何かに変更することをお勧めします。
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
Gillesの回答の一般化バージョン、最初のパラメーターは一致を見つけるために使用されます。
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
シンボリックリンクを使用し続けます。
見つけることができません。シェルループよりも単純なものは考えられません。(未テスト、ないと仮定/.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%/*}
拡張グロビングを有効にして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に一致する0個以上のディレクトリで構成されるパスに一致します。短縮形として、**/
と同等(*/)#
です。修飾子
オプションの単語指定子の後に、次の1つ以上の修飾子のシーケンスを追加できます。各修飾子の前には「:」が付きます。これらの修飾子は、特に明記されている場合を除き、ファイル名の生成とパラメーターの展開の結果にも機能します。
- a
- ファイル名を絶対パスに変換します。必要に応じて現在のディレクトリを追加し、「..」と「。」の使用を解決します。
- A
- ' a 'ですが、可能な場合はシンボリックリンクの使用も解決します。「..」の解決は、シンボリックリンクの解決の前に発生することに注意してください。この呼び出しは、
realpath
システムにシステムコールがない場合(近代的なシステムにはない場合)と同等です。- h
- 末尾のパス名コンポーネントを削除して、頭を残します。これは '
dirname
'のように機能します。
クレジット:Faux
を#zsh
使用する最初の提案に対して(../)#.git(:h)
。
(../)
をしているのか...ああ、誰/何Faux on #zsh
ですか?
(../)
だけでは意味がありませんが(../)#
、括弧内のパターンを0回以上展開し、ファイルシステムに実際に存在するもののみを使用することを意味します。親ディレクトリを0からnに拡張しているため、ファイルシステムのルートまで効果的に検索します(注:パターンの後に何かを追加して意味のあるものにすることをお勧めしますが、啓発のために実行しますprint -l (../)#
)。そして#zsh
、IRCチャンネルでFaux
あり、そのユーザー名です。 Faux
解決策、したがってクレジットを提案しました。
(../)#.git(/Y1:a:h)
(zshの最近のバージョンで)最初に見つかったの1で停止する
setopt extended_glob
このバージョンのfindupは、@ sinelawの答えのような「find」構文をサポートしますが、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プロジェクトにシンボリックリンクを使用しています。$ GOPATH / srcにプロジェクトを作成し、〜/ projectsにシンボリックリンクします。したがって、実行git rev-parse --show-toplevel
すると〜/ projectsディレクトリではなく、$ GOPATHディレクトリが出力されます。このソリューションはその問題を解決します。
これは非常に特殊な状況であることを認識していますが、解決策は価値があると思います。
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