web-dev-qa-db-ja.com

サブディレクトリではなく親ディレクトリを検索する

私はファイルツリーの深い位置にネストされており、どの親ディレクトリにファイルが含まれているのかを知りたいのですが。

例えば。ネストされたGITリポジトリのセットを使用していて、現在のファイルを制御している.gitディレクトリを見つけたいと考えています。私はのようなものを望みます

find -searchup -iname ".git"
46
Vincent Scheib

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は、ほとんどのプラットフォームにデフォルトでインストールされていません。

16
sinelaw
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
30
ephemient

Gillesの回答の一般化バージョン。一致を見つけるために使用される最初のパラメーター:

find-up () {
  path=$(pwd)
  while [[ "$path" != "" && ! -e "$path/$1" ]]; do
    path=${path%/*}
  done
  echo "$path"
}

シンボリックリンクの使用を維持します。

20
Vincent Scheib

見つけることはできません。シェルループよりも単純なものは考えられません。 (テストされていない、/.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個以上のディレクトリで構成されるパスに一致します。省略表現として、**/(*/)#と同等です。

修飾子

オプションのWord指定子の後に、次の修飾子のシーケンスを追加できます。各修飾子の前には「:」を付けます。これらの修飾子は、特に明記されていない限り、ファイル名の生成とパラメータの展開の結果にも作用します。

  • a
    • ファイル名を絶対パスに変換します。必要に応じて、現在のディレクトリの前に付加し、「..」および「。」の使用を解決します。
  • [〜#〜] a [〜#〜]
    • 'a'と同様ですが、可能な場合はシンボリックリンクの使用も解決します。 「..」の解決は、シンボリックリンクの解決の前に発生することに注意してください。この呼び出しは、システムにrealpathシステムコールがない場合(現代のシステムにはあります)を除いて、と同等です。
  • h
    • 頭を残して、末尾のパス名コンポーネントを削除します。これは「dirname」のように機能します。

クレジット:#zshの使用に関する最初の提案として、(../)#.git(:h)Faux

12
unthought

シンボリックリンクを使用すると、他のオプションの一部が無効になることがわかりました。特にgit固有の答え。私はかなり効率的な this 元の回答から自分のお気に入りを途中で作成しました。

#!/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は特定の場所にソースコードを配置する必要があり、プロジェクトを〜/ projectsの下に置きたいので、私のgoプロジェクトにシンボリックリンクを使用しています。 $ GOPATH/srcにプロジェクトを作成し、〜/ projectsにシンボリックリンクします。実行中git rev-parse --show-toplevelは、〜/ projectsディレクトリではなく、$ GOPATHディレクトリを出力します。このソリューションはその問題を解決します。

これは非常に特殊な状況だと思いますが、解決策は価値があると思います。

0
blockloop

再帰により、非常に簡潔なソリューションが得られます。

#!/usr/bin/env bash

parent-find() {
  local file="$1"
  local dir="$2"

  test -e "$dir/$file" && echo "$dir" && return 0
  [ '/' = "$dir" ] && return 1

  parent-find "$file" "$(dirname "$dir")"
}

# Example
parent-find .bashrc "/home/user/projects/parent-find" 
# should respond with "/home/user"
0
bas080

Vincent Scheibの solution は、ルートディレクトリにあるファイルに対しては機能しません。

次のバージョンでは、最初の引数として最初のディレクトリを渡すことができます。

find-up() {
    path="$(realpath -s "$1")"

    while ! [ -e "$path"/"$2" ] && [ -n "$path" ]; do
        path="${path%/*}"
    done

    [ -e "$path"/"$2" ] && echo "$path"/"$2"
}
0
Fabio A.

このバージョンのfindupは、@ 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
0
Erik Aronesty

sinelaw's solution を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
0
dosentmatter