ค้นหาการค้นหาในไดเรกทอรีหลักแทนไดเรกทอรีย่อย


45

ฉันซ้อนอยู่ในแผนผังไฟล์และฉันต้องการค้นหาไดเรกทอรีหลักที่มีไฟล์อยู่

เช่นฉันอยู่ในกลุ่มของที่เก็บ Git ซ้อนกันและต้องการค้นหาไดเรกทอรี. git ที่ควบคุมไฟล์ที่ฉันกำลังอยู่ ฉันหวังว่าจะได้พบกับบางสิ่งบางอย่างเช่น -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สคริปต์ข้างต้นจะติดตาม symlink ในทางขึ้นดังที่ระบุไว้ในความคิดเห็น คุณสามารถใช้realpath -sแทนหากคุณต้องการติดตามพา ธ ด้วยชื่อ ("/ foo / bar" จะไปถึง "foo" แม้ว่า "bar" เป็น symlink) - อย่างไรก็ตามการติดตั้งrealpathที่ไม่ได้ติดตั้งโดยค่าเริ่มต้น แพลตฟอร์มส่วนใหญ่


ปัญหาเหล่านี้คือวิธีแก้ไขลิงก์ ตัวอย่างเช่นหากฉันอยู่ใน ~ / foo / bar และ bar เป็น symlink ไปยัง / some / other / place ดังนั้น ~ / foo และ ~ / จะไม่ถูกค้นหา
Erik Aronesty

@ErikAronesty คุณพูดถูก - อัพเดตคำตอบ คุณสามารถใช้realpath -sเพื่อให้ได้พฤติกรรมที่คุณต้องการ
sinelaw

ที่ทำงานเมื่อคุณระบุเส้นทางแบบเต็มเพื่อค้นหา น่าเศร้าที่ centos 5.8 realpath -s <dir> มีข้อผิดพลาดที่ถ้า <dir> สัมพันธ์กันมันจะแก้ไข symlinks ต่อไป ... แม้จะเป็นเอกสารก็ตาม
Erik Aronesty

readlinkไม่ได้เป็นส่วนหนึ่งของมาตรฐาน สคริปต์แบบพกพาสามารถใช้งานได้กับคุณสมบัติเชลล์ POSIX เท่านั้น
schily

1
FYI นี้ไม่ได้ทำงานใน zsh เพราะมันนิยามใหม่ของ $ เส้นทางเพื่อให้เปลือกไม่สามารถหาหรือfind readlinkฉันขอแนะนำให้เปลี่ยนเป็นอย่างอื่น$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
ใช้งานได้เฉพาะกับคอมไพล์เท่านั้น คำถามทั่วไปมากขึ้น
alex

1
สิ่งนี้จะไม่ทำงานใน repo ที่เปลือยเปล่า
Jason Pyeron

19

พารามิเตอร์ทั่วไปของคำตอบของ Gilles ซึ่งเป็นพารามิเตอร์แรกที่ใช้ในการค้นหาการจับคู่:

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

คงการใช้งาน sym-links


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 ในฐานะที่เป็นชวเลข, เทียบเท่ากับ**/(*/)#

การปรับเปลี่ยน

หลังจากตัวออกแบบคำที่ไม่จำเป็นคุณสามารถเพิ่มลำดับของโมเดอเรเตอร์ต่อไปนี้หนึ่งตัวหรือมากกว่าโดยแต่ละอันนำหน้าด้วย ':' ตัวดัดแปลงเหล่านี้ยังทำงานกับผลลัพธ์ของการสร้างชื่อไฟล์และการขยายพารามิเตอร์ยกเว้นที่ระบุไว้

  • a
    • เปลี่ยนชื่อไฟล์ให้เป็นพา ธ สัมบูรณ์: ผนวกไดเรกทอรีปัจจุบันหากจำเป็นและแก้ไขการใช้ '.. ' และ 'ใด ๆ '
  • A
    • ในฐานะ ' a ' แต่ยังแก้ไขการใช้ลิงก์สัญลักษณ์หากเป็นไปได้ โปรดทราบว่าความละเอียดของ '.. ' เกิดขึ้นก่อนการแก้ไขลิงก์สัญลักษณ์ การเรียกนี้เทียบเท่ากับ a เว้นแต่ว่าระบบของคุณมีการrealpathเรียกของระบบ (ระบบที่ทันสมัยทำ)
  • ชั่วโมง
    • ลบคอมโพเนนต์ชื่อพา ธ ที่ต่อท้ายออกจากส่วนหัว ใช้งานได้เช่น ' dirname'

เครดิต: Fauxบนสำหรับข้อเสนอแนะของการเริ่มต้นของการใช้#zsh(../)#.git(:h)


นี่เป็นเรื่องที่น่าอัศจรรย์ ... ถ้าฉันสามารถคิดออกเท่านั้น (คำใบ้: คุณควรจะบอกฉัน ) (../)กำลังทำอะไรอยู่ ... โอ้และใคร / คือFaux on #zshอะไร?
alex grey

1
@alexgray (../)เพียงอย่างเดียวไม่ได้มีความหมายอะไรมาก แต่(../)#หมายถึงพยายามขยายรูปแบบในวงเล็บ 0 หรือมากกว่านั้นและใช้เฉพาะรูปแบบที่มีอยู่จริงในระบบไฟล์ เนื่องจากเรากำลังขยายจากไดเรกทอรีหลัก 0 ถึง n เราจึงค้นหาขึ้นไปสู่รูทของระบบไฟล์ได้อย่างมีประสิทธิภาพ (หมายเหตุ: คุณอาจต้องการเพิ่มบางสิ่งหลังจากรูปแบบเพื่อให้มีความหมาย แต่สำหรับการดำเนินการตรัสรู้print -l (../)#) และ#zshเป็นช่อง IRC Fauxเป็นชื่อผู้ใช้ในนั้น Fauxแนะนำวิธีการแก้ปัญหาดังนั้นเครดิต
unthought

1
ที่จะขยายทั้งหมดของพวกเขา คุณอาจต้องการ: (../)#.git(/Y1:a:h)เพื่อหยุดที่พบครั้งแรกหนึ่ง (กับรุ่นล่าสุดของ zsh)
Stéphane Chazelas

1
มีประโยชน์มากขอบคุณ เพื่อเปิดใช้งานการขยายแบบวงกลมทำsetopt extended_glob
Shlomi

0

การค้นหารุ่นนี้รองรับไวยากรณ์ "find" เช่นคำตอบของ @ sinelaw แต่ยังรองรับ symlink โดยไม่จำเป็นต้องใช้ realpath นอกจากนี้ยังสนับสนุนฟีเจอร์ "หยุดที่" ซึ่งเป็นทางเลือกดังนั้นจึงใช้งานได้: findup .:~ -name foo... ค้นหา foo โดยไม่ผ่าน dir บ้าน

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

ฉันพบว่าการทำงานกับ symlink นั้นจะฆ่าตัวเลือกอื่น ๆ โดยเฉพาะคำตอบเฉพาะคอมไพล์ ฉันได้สร้างลงครึ่งหนึ่งจากที่ชื่นชอบของตัวเองนี้คำตอบเดิมที่มีประสิทธิภาพสวย

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

ฉันใช้ symlinks สำหรับโครงการ go ของฉันเพราะ go ต้องการซอร์สโค้ดในบางตำแหน่งและฉันต้องการให้โปรเจคของฉันอยู่ภายใต้ ~ / projects ฉันสร้างโครงการใน $ GOPATH / src และเชื่อมโยงโครงการเข้ากับ ~ / โครงการ ดังนั้นการเรียกใช้จะgit rev-parse --show-toplevelพิมพ์ไดเรกทอรี $ GOPATH ไม่ใช่ไดเรกทอรี ~ / projects วิธีนี้แก้ปัญหานั้นได้

ฉันรู้ว่านี่เป็นสถานการณ์ที่เฉพาะเจาะจงมาก แต่ฉันคิดว่าทางออกมีค่า


0

โซลูชันของ Vincent Scheib ไม่สามารถใช้งานกับไฟล์ที่อยู่ในไดเรกทอรีรากได้

รุ่นต่อไปนี้ทำและยังช่วยให้คุณผ่าน dir เริ่มต้นเป็นอาร์กิวเมนต์ที่ 1

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 มันไม่ได้ติดตาม symlinks และรวมถึงการค้นหาไดเรกทอรีรูทโดยใช้ลูป 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.