하위 디렉토리 대신 상위 디렉토리에서 검색 찾기


45

파일 트리에 깊게 중첩되어 있으며 파일이 들어있는 상위 디렉토리를 찾고 싶습니다.

예를 들어 중첩 된 Git 리포지토리에 있고 현재있는 파일을 제어하는 ​​.git 디렉토리를 찾고 싶습니다. find -searchup -iname ".git"과 같은 것을 원합니다.

답변:


16

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이름으로 경로를 따라 가려면 대신 사용할 수 있습니다 ( "bar"가 symlink 인 경우에도 "/ foo / bar"는 "foo"로 올라갑니다). 그러나 realpath기본적으로 설치되어 있지 않은 설치가 필요합니다 . 대부분의 플랫폼.


이것의 문제점은 링크를 해결하는 방법입니다. 예를 들어 ~ / foo / bar에 있고 bar가 / some / other / place에 대한 심볼릭 링크 인 경우 ~ / foo 및 ~ /는 검색되지 않습니다.
Erik Aronesty 2016 년

@ErikAronesty, 당신 말이 맞아-답변을 업데이트했습니다. realpath -s원하는 동작을 얻는 데 사용할 수 있습니다 .
sinelaw

검색 할 전체 경로를 지정할 때 작동합니다. 슬프게도 centos 5.8에서 realpath -s <dir>에는 <dir>이 상대적이면 문서화에도 불구하고 어쨌든 심볼릭 링크를 해결하는 버그가 있습니다.
Erik Aronesty 2016 년

readlink표준의 일부가 아닙니다. POSIX 쉘 기능만으로 이식 가능한 스크립트를 구현할 수 있습니다.
schily

1
쉘이 찾을 수 있도록이 $ 경로를 재정의하기 때문에 참고로,이 zsh을 작동하지 않습니다 findreadlink. $curpath쉘 변수를 가리지 않도록 대신 비슷한 것으로 변경하는 것이 좋습니다 .
Skyler

28
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

4
이것은 git 자체에서만 작동합니다. 질문은 더 일반적입니다.
alex

1
맨손으로
리포지토리

19

일치를 찾는 데 사용되는 첫 번째 매개 변수 인 Gilles의 답변에 대한 일반화 된 버전 :

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

sym-link를 계속 사용합니다.


15

찾을 수 없습니다. 쉘 루프보다 단순한 것을 생각할 수 없습니다. (테스트되지 않은 것으로 가정 /.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%/*}

1
쿨, 이것은 일반적인 해결책을위한 길을 열어줍니다-여기에 다른 답변으로 게시합니다.
Vincent Scheib

12

확장 글 로빙이 활성화 된 상태에서 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 개 이상의 디렉토리로 구성된 경로와 일치합니다. 줄임말 **/로와 같습니다 (*/)#.

수정 자

선택적인 단어 지정자 뒤에 각각 ':'이 오는 하나 이상의 다음 수정 자 시퀀스를 추가 할 수 있습니다. 이러한 수정자는 언급 된 경우를 제외하고 파일 이름 생성 및 매개 변수 확장 결과에서도 작동합니다.

  • 에이
    • 파일 이름을 절대 경로로 바꾸십시오. 필요한 경우 현재 디렉토리 앞에 붙고 '..'및 '.'사용을 해결합니다.
  • 에이
    • ' a '로 가능하지만 가능한 경우 심볼릭 링크 사용을 해결하십시오. '..'의 해상도는 심볼릭 링크의 해상도보다 먼저 발생합니다. 이 호출은 시스템에 realpath시스템 호출이없는 한 (현대 시스템에서는) 않는 것과 같습니다 .
  • h
    • 머리를 남기고 후행 경로 이름 구성 요소를 제거하십시오. 이것은 ' dirname' 처럼 작동합니다 .

크레딧 : Faux을 ( #zsh를) 사용하는 초기 제안 에 대해 (../)#.git(:h).


이건 정말 대단합니다 ... 만약 내가하고있는 일을 알아낼 수 있다면 (힌트 : 말해야합니다 ) (../)... 아, 그리고 누가 / 무엇 Faux on #zsh입니까?
alex gray

1
@alexgray (../)만으로는 큰 의미가 없지만 (../)#괄호 안의 패턴을 0 번 이상 확장하고 실제로 파일 시스템에 존재하는 패턴 만 사용하십시오. 우리는 0에서 n까지의 상위 디렉토리로 확장하고 있기 때문에 파일 시스템의 루트까지 효과적으로 검색합니다 (참고 : 패턴 뒤에 의미를 부여하기 위해 무언가를 추가하고 계몽 실행을 원할 것입니다 print -l (../)#). 그리고 #zshIRC 채널이며, Faux그것에 대한 사용자 이름입니다. Faux해결책을 제안, 따라서 신용.
unthought

1
그것은 그들 모두를 확장시킬 것입니다. 당신은 원할지도 모릅니다 : (../)#.git(/Y1:a:h)(최신 버전의 zsh로) 처음 발견 된 것에서 멈추기
Stéphane Chazelas

1
매우 도움이되었습니다. 감사합니다. 확장 글 로빙을 활성화하려면setopt extended_glob
Shlomi

0

이 버전의 findup은 @sinelaw의 답변과 같은 "find"구문을 지원하지만 realpath가 필요없는 symlink도 지원합니다. 또한 선택적 "stop at"기능을 지원하므로 다음과 같이 작동합니다. 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

symlinks로 작업하면 다른 옵션 중 일부가 죽는다는 것을 알았습니다. 특히 자식 전용 답변. 나는 절반 의 효율성 을 가진 독창적 인 답변에서 자신이 좋아하는 것을 만들었습니다 .

#!/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에 심볼릭 링크합니다. 따라서 running git rev-parse --show-toplevel은 ~ / projects 디렉토리가 아닌 $ GOPATH 디렉토리를 인쇄합니다. 이 솔루션은 해당 문제를 해결합니다.

나는 이것이 매우 구체적인 상황이라는 것을 알고 있지만 솔루션이 가치 있다고 생각합니다.


0

Vincent Scheib의 솔루션 은 루트 디렉토리에있는 파일에는 작동하지 않습니다.

다음 버전에서는 시작 디렉토리를 첫 번째 인수로 전달할 수 있습니다.

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

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

    [ -e "$path"/"$2" ] && echo "$path"/"$2"
}

0

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.