alt dizinler yerine üst dizinlerde arama bulma


45

Bir dosya ağacının derinliklerinde yuvalarım ve hangi üst dizinin bir dosya içerdiğini bulmak istiyorum.

Örneğin, bir yuvalanmış Git veri havuzundayım ve şu anda bulunduğum dosyaları kontrol eden .git dizinini bulmak istiyorum. -Arama -iname ".git" bulma gibi bir şey umuyorum

Yanıtlar:


16

findSeçeneklerin kullanılmasına izin veren daha genel bir versiyon :

#!/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

Örneğin (betiğin olarak kaydedildiğini varsayarsak find_up.sh)

find_up.sh some_dir -iname "foo*bar" -execdir pwd \;

... desenli bir dosyanın bulunduğu tüm some_dirataların (kendisi dahil) isimlerini yazdıracaktır /.

readlink -fYukarıdaki betiği kullanırken yorumlarda belirtildiği gibi, yukarı çıkan sembolik izleri takip edecek. Sen kullanabilirsiniz realpath -sadıyla yolları takip etmek istiyorsanız, bunun yerine ( "/ foo / bar" "bar" sembolik olsa bile "foo" kadar gidecek) - ancak yüklemeyi gerektiren realpathvarsayılan olarak yüklü değildir çoğu platformda.


bunlarla ilgili sorun, bağlantıları çözme şeklidir. örneğin, ~ / foo / bar içindeysem ve bar / / / diğer / yer ile bir bağlantı ise, o zaman ~ / foo ve ~ / aranmaz.
Erik Aronesty,

@ErikAronesty, haklısın - cevap güncellendi. İstediğiniz realpath -sdavranışı elde etmek için kullanabilirsiniz .
sinelaw

tam bir yol belirtmeniz gerektiğinde çalışır. ne yazık ki, 5.8 numaralı centos'ta realpath -s <dir> 'in <dir>' nin göreceli olması durumunda, belgelemesine rağmen sembolik bağları yine de çözer.
Erik Aronesty,

readlinkstandardın bir parçası değildir. Taşınabilir bir komut dosyası yalnızca POSIX kabuk özellikleriyle uygulanabilir.
schily

1
Bilginize, bu zsh içinde çalışmaz çünkü $ bulgusunu yeniden tanımlar, böylece kabuk bulamaz findveya readlink. Bunun $curpathyerine bunun gibi bir şeyi değiştirmenizi öneririm, böylece kabuk değişkenini gölgelendirmez.
Skyler

28
git rev-parse --show-toplevel

eğer birinde iseniz, geçerli havuzun üst düzey dizinini yazdıracaktır.

Diğer ilgili seçenekler:

# `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
Bu sadece git kendisi için çalışır. Soru daha geneldir.
alex

1
Bu çıplak bir
depoda

19

Gilles 'cevabının genelleştirilmiş bir versiyonu, eşleşmeyi bulmak için kullanılan ilk parametre:

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

Sym-link kullanımını sağlar.


15

Bul, yapamaz. Bir kabuk döngüsünden daha basit bir şey düşünemiyorum. (Denenmemiş, hayır olduğunu varsayarız /.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 deposunun özel durumu için git'in işi sizin için yapmasına izin verebilirsiniz.

git_root=$(GIT_EDITOR=echo git config -e)
git_root=${git_root%/*}

1
Güzel, bu bana başka bir cevap olarak gönderdiğim genel bir çözüm yolunda.
Vincent Scheib

12

Genişletilmiş globbing etkinleştirilmiş bir zsh kullanıyorsanız, bir oneliner ile yapabilirsiniz:

(../)#.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'

Açıklama (alıntı yapılan man zshexpn):

Özyinelemeli Küreselleşme

Formun bir yol adı bileşeni, (foo/)#foo deseni ile eşleşen sıfır veya daha fazla dizinden oluşan bir yolla eşleşir. Bir kestirme olarak, **/eşittir (*/)#.

Düzenleyiciler

İsteğe bağlı sözcük tanımlayıcısından sonra, her biri ':' ile gösterilen aşağıdaki değiştiricilerden birini veya daha fazlasını içeren bir dizi ekleyebilirsiniz. Bu değiştiriciler ayrıca, belirtilenler dışında dosya adı oluşturma ve parametre genişletme işlemlerinin sonucu üzerinde çalışır.

  • bir
    • Bir dosya adını mutlak bir yola dönüştürün: gerekirse geçerli dizini hazırlar ve '..' ve '.'
  • bir
    • ' A ' olarak, ancak mümkün olduğunda sembolik bağların kullanımını da çöz. Sembolik linklerin çözünürlüğünden önce '..' çözünürlüğünün oluştuğunu unutmayın. Bu çağrı, sisteminizde realpathsistem çağrısı olmadıkça (modern sistemler yapar) eşdeğerdir .
  • h
    • Baştaki bırakarak takip eden bir yol adı bileşeni çıkarın. Bu ' dirname' gibi çalışır .

Kredi: Fauxkullanım #zshilk önerisi için açık (../)#.git(:h).


Bu şaşırtıcı ... Sadece bir şey çözebilseydim (ipucu: bana söylemelisin ) ne (../)yapıyor ... Oh, kim / ne Faux on #zsh?
alex grey

1
@alexgray (../)tek başına pek bir şey ifade etmiyor, ancak (../)#parantez içindeki deseni 0 veya daha fazla kez genişletmeyi denemek ve yalnızca dosya sisteminde gerçekten var olanları kullanmak anlamına gelir. 0'dan n'ye kadar ana dizini genişlettiğimiz için, dosya sisteminin kökünü yukarı doğru etkili bir şekilde araştırıyoruz (not: kalıptan sonra anlamlı kılmak için bir şey eklemek istiyorsunuz, ancak aydınlanmanın çalışması için print -l (../)#). Ve #zshbir IRC kanalı, Fauxüzerinde bir kullanıcı adı. Fauxçözüm önerdi, bu nedenle kredi.
düşünülmemiş

1
Bu hepsini genişletecek. Sen isteyebilirsiniz: (../)#.git(/Y1:a:h)(zsh güncel bir sürümü ile) ilk bulduğu durmak
Stéphane Chazelas

1
Çok yararlı, teşekkürler. Genişletilmiş globbing işlemini etkinleştirmek içinsetopt extended_glob
Shlomi

0

Findup'ın bu sürümü @ sinelaw'ın cevabı gibi "find" sözdizimini destekler, fakat aynı zamanda realpath'a ihtiyaç duymadan sembolik bağlantıları destekler. Ayrıca isteğe bağlı bir "durma" özelliğini de destekler, bu nedenle çalışır: findup .:~ -name foo... ana dizini geçmeden foo arar.

#!/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

Sembollerle çalışmanın diğer seçeneklerden bazılarını öldürdüğünü gördüm. Özellikle git belirli cevaplar. Yarı yolda , oldukça etkili olan bu orijinal cevaptan kendi favorimi yarattım .

#!/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 projelerim için sembolik bağlantılar kullanıyorum, çünkü go belirli bir konumda kaynak kodu istiyor ve projelerimi ~ / projects altında tutmayı seviyorum. Projeyi $ GOPATH / src ile oluşturuyorum ve bunları ~ / projects ile birleştiriyorum. Böylece çalışan git rev-parse --show-toplevel~ / projects dizinini değil $ GOPATH dizinini yazdırıyor. Bu çözüm bu sorunu çözdü.

Bunun çok özel bir durum olduğunun farkındayım, ancak çözümün değerli olduğunu düşünüyorum.


0

Vincent Scheib'in çözümü , kök dizinde bulunan dosyalar için çalışmıyor.

Aşağıdaki sürüm aynı zamanda başlangıç ​​direktörünü 1. argüman olarak geçmenizi sağlar.

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

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

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

0

Sinelaw'un çözümünü POSIX olacak şekilde değiştirdim . Sembolik linkleri takip etmez ve do while döngüsünü kullanarak kök dizini aramayı içerir.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.