उपनिर्देशिकाओं के बजाय मूल निर्देशिका में खोज करना


45

मैं एक फ़ाइल के पेड़ में गहरी नेस्टेड हूं, और मैं यह खोजना चाहूंगा कि किस मूल निर्देशिका में एक फ़ाइल है।

उदाहरण के लिए, मैं नेस्टेड जीआईटी रिपॉजिटरी के एक सेट में हूं। वर्तमान में मैं जिन फाइलों को नियंत्रित कर रहा हूं, उनके लिए .IT निर्देशिका को खोजना चाहते हैं। मुझे उम्मीद है कि कुछ मिल जाएगा जैसे -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इसके बजाय उपयोग कर सकते हैं , यदि आप नाम से पथों का अनुसरण करना चाहते हैं ("/ foo / bar" "foo" तक जाएगा, भले ही "बार" एक सिम्लिंक हो) - हालाँकि जिसे स्थापित करने की आवश्यकता होती realpathहै जिसे डिफ़ॉल्ट रूप से स्थापित नहीं किया जाता है अधिकांश प्लेटफार्मों।


इसके साथ परेशानी यह है कि यह लिंक को हल करता है। उदाहरण के लिए अगर मैं ~ / foo / बार में हूं, और बार / कुछ / अन्य / जगह पर एक सिमलिंक है, तो ~ / foo और ~ / कभी नहीं खोजा जाएगा।
एरिक एरोनेस्टी

@ ErikAronesty, आप सही हैं - जवाब अपडेट किया गया। आप अपने realpath -sइच्छित व्यवहार को प्राप्त करने के लिए उपयोग कर सकते हैं।
सिनेलॉ

जब आप खोज करने के लिए पूर्ण पथ निर्दिष्ट करते हैं तो यह काम करता है। दुख की बात है कि, सेंटोस 5.8 पर, Realpath -s <dir> के पास एक बग है जहां अगर <dir> सापेक्ष है, तो यह वैसे भी सहानुभूति का समाधान करता है ... इसके बावजूद यह दस्तावेज है।
एरिक एरोनिटी

readlinkमानक का हिस्सा नहीं है। एक पोर्टेबल स्क्रिप्ट को केवल POSIX शेल विशेषताओं के साथ लागू किया जा सकता है।
स्किल

1
FYI करें, यह zsh में काम नहीं करता है क्योंकि यह $ पथ को पुनर्परिभाषित करता है इसलिए शेल नहीं मिल सकता है findया readlink। मेरा सुझाव है कि इसे $curpathबदले की तरह कुछ बदल दिया जाए ताकि यह शेल चर को छाया न दे।
स्काइलर

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
यह केवल गिट के लिए काम करता है। प्रश्न अधिक सामान्य है।
एलेक्स

1
यह नंगे रेपो में काम नहीं करेगा,
जेसन पीरोन

19

गाइल्स के उत्तर का एक सामान्यीकृत संस्करण, पहला पैरामीटर जिसका उपयोग मैच खोजने के लिए किया जाता है:

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

सिम्स-लिंक का उपयोग करता है।


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
कूल, यह मुझे एक सामान्य समाधान के लिए एक मार्ग पर रखता है - जिसे मैं यहां एक और उत्तर के रूप में पोस्ट करता हूं।
विन्सेन्ट स्कीब

12

यदि आप विस्तारित ग्लोबिंग सक्षम के साथ zsh का उपयोग कर रहे हैं, तो आप इसे एक ऑनलाइनर के साथ कर सकते हैं:

(../)#.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/)#एक पथ से मेल खाता है जिसमें पैटर्न फू से मेल खाते शून्य या अधिक निर्देशिका शामिल हैं। एक आशुलिपि के रूप में, **/के बराबर है (*/)#

संशोधक

वैकल्पिक शब्द पदनाम के बाद, आप निम्नलिखित संशोधक में से एक या एक से अधिक का क्रम जोड़ सकते हैं, प्रत्येक एक ':' से पहले होगा। ये संशोधक फ़ाइलनाम निर्माण और पैरामीटर विस्तार के परिणाम पर भी काम करते हैं, सिवाय जहां नोट किए गए।

    • एक फ़ाइल नाम को एक पूर्ण पथ में बदलें: यदि आवश्यक हो, तो वर्तमान निर्देशिका को प्रस्तुत करता है, और '..' और '' के किसी भी उपयोग को हल करता है।
    • ' ' के रूप में, लेकिन जहां संभव हो, प्रतीकात्मक लिंक का उपयोग भी हल करें। ध्यान दें कि प्रतीकात्मक लिंक के समाधान से पहले '..' का संकल्प होता है। यह कॉल तब तक के बराबर है जब तक कि आपके सिस्टम में realpathसिस्टम कॉल नहीं है (आधुनिक सिस्टम करते हैं)।
    • सिर को छोड़कर, एक पीछे चल रहे घटक का घटक निकालें। यह ' dirname' की तरह काम करता है ।

क्रेडिट: Fauxपर #zshका उपयोग करने का प्रारंभिक सुझाव के लिए (../)#.git(:h)


यह आश्चर्यजनक है ... अगर मैं केवल यह पता लगा सकता था (संकेत: आपको मुझे बताना चाहिए ) क्या (../)कर रहा है ... ओह, और कौन / क्या है Faux on #zsh?
एलेक्स ग्रे

1
@alexgray (../)अकेले बहुत मायने नहीं रखता है, लेकिन (../)#इसका मतलब है कि कोष्ठक 0 या अधिक बार के अंदर पैटर्न का विस्तार करने की कोशिश करें, और केवल उन लोगों का उपयोग करें जो वास्तव में फाइल सिस्टम पर मौजूद हैं। क्योंकि हम 0 से n मूल निर्देशिका में विस्तार कर रहे हैं, हम प्रभावी रूप से फ़ाइल सिस्टम की जड़ में ऊपर की ओर खोजते हैं (ध्यान दें: आप संभवतः इसे सार्थक बनाने के लिए पैटर्न के बाद कुछ जोड़ना चाहते हैं, लेकिन आत्मज्ञान निष्पादित के लिए print -l (../)#)। और #zshएक आईआरसी चैनल है, इस Fauxपर एक उपयोगकर्ता नाम है। Fauxसमाधान का सुझाव दिया, इसलिए क्रेडिट।
अंक

1
यह उन सभी का विस्तार करेगा। आप चाहते हो सकता है: (../)#.git(/Y1:a:h)पहले पाए गए (एक हाल के संस्करण के साथ) को रोकने के लिए
स्टीफन चेज़लस

1
बहुत मददगार, धन्यवाद। विस्तारित ग्लोबिंग को सक्षम करने के लिएsetopt extended_glob
श्लोमी

0

खोज का यह संस्करण @ sinelaw के उत्तर की तरह "खोज" वाक्यविन्यास का समर्थन करता है, लेकिन यह भी realpath की आवश्यकता के बिना सहानुभूति का समर्थन करता है। यह एक वैकल्पिक "स्टॉप एट" फीचर का भी समर्थन करता है, इसलिए यह काम करता है: findup .:~ -name 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

मैंने पाया है कि सिम्बलिंक्स के साथ काम करने से कुछ अन्य विकल्प खत्म हो जाते हैं। विशेषकर गिट विशिष्ट जवाब। मैंने आधे से अपना पसंदीदा इस मूल उत्तर से बनाया है जो बहुत ही आकर्षक है।

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

मैं अपने गो प्रोजेक्ट्स के लिए सिमलींक का उपयोग कर रहा हूं क्योंकि गो एक निश्चित स्थान पर स्रोत कोड चाहता है और मैं अपनी परियोजनाओं को ~ / परियोजनाओं के तहत रखना पसंद करता हूं। मैं $ GOPATH / src में प्रोजेक्ट बनाता हूं और उन्हें ~ / प्रोजेक्ट्स को सिमिलिंक करता हूं। इसलिए रनिंग git rev-parse --show-toplevel$ GOPATH डायरेक्टरी को प्रिंट करता है, न कि ~ / प्रोजेक्ट्स डायरेक्टरी को। यह समाधान उस समस्या को हल करता है।

मुझे लगता है कि यह एक बहुत ही विशिष्ट स्थिति है, लेकिन मुझे लगता है कि समाधान मूल्यवान है।


0

विन्सेन्ट स्कीब का समाधान रूट डायरेक्टरी में रहने वाली फ़ाइलों के लिए काम नहीं करता है।

निम्न संस्करण करता है, और आपको शुरुआती तर्क को 1 तर्क के रूप में पास करने देता है।

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

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

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

0

मैंने पोसिक्स होने के लिए साइनेलॉ के समाधान को संशोधित किया । यह सिर्लिंक का पालन नहीं करता है और इसमें लूप करते समय रूट निर्देशिका का उपयोग करना शामिल है।

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.