ฉันซ้อนอยู่ในแผนผังไฟล์และฉันต้องการค้นหาไดเรกทอรีหลักที่มีไฟล์อยู่
เช่นฉันอยู่ในกลุ่มของที่เก็บ Git ซ้อนกันและต้องการค้นหาไดเรกทอรี. git ที่ควบคุมไฟล์ที่ฉันกำลังอยู่ ฉันหวังว่าจะได้พบกับบางสิ่งบางอย่างเช่น -searchup -iname ".git"
ฉันซ้อนอยู่ในแผนผังไฟล์และฉันต้องการค้นหาไดเรกทอรีหลักที่มีไฟล์อยู่
เช่นฉันอยู่ในกลุ่มของที่เก็บ Git ซ้อนกันและต้องการค้นหาไดเรกทอรี. git ที่ควบคุมไฟล์ที่ฉันกำลังอยู่ ฉันหวังว่าจะได้พบกับบางสิ่งบางอย่างเช่น -searchup -iname ".git"
คำตอบ:
รุ่นทั่วไปยิ่งขึ้นที่อนุญาตให้ใช้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
ที่ไม่ได้ติดตั้งโดยค่าเริ่มต้น แพลตฟอร์มส่วนใหญ่
realpath -s
เพื่อให้ได้พฤติกรรมที่คุณต้องการ
readlink
ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน สคริปต์แบบพกพาสามารถใช้งานได้กับคุณสมบัติเชลล์ POSIX เท่านั้น
find
readlink
ฉันขอแนะนำให้เปลี่ยนเป็นอย่างอื่น$curpath
แทนดังนั้นจึงไม่ทำให้ตัวแปรเชลล์เป็นเงา
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
พารามิเตอร์ทั่วไปของคำตอบของ Gilles ซึ่งเป็นพารามิเตอร์แรกที่ใช้ในการค้นหาการจับคู่:
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
คงการใช้งาน sym-links
ค้นหาไม่สามารถทำได้ ฉันไม่สามารถคิดอะไรที่ง่ายไปกว่าเปลือกวง (ยังไม่ได้ทดสอบถือว่าไม่มี/.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 ในฐานะที่เป็นชวเลข, เทียบเท่ากับ**/
(*/)#
การปรับเปลี่ยน
หลังจากตัวออกแบบคำที่ไม่จำเป็นคุณสามารถเพิ่มลำดับของโมเดอเรเตอร์ต่อไปนี้หนึ่งตัวหรือมากกว่าโดยแต่ละอันนำหน้าด้วย ':' ตัวดัดแปลงเหล่านี้ยังทำงานกับผลลัพธ์ของการสร้างชื่อไฟล์และการขยายพารามิเตอร์ยกเว้นที่ระบุไว้
- a
- เปลี่ยนชื่อไฟล์ให้เป็นพา ธ สัมบูรณ์: ผนวกไดเรกทอรีปัจจุบันหากจำเป็นและแก้ไขการใช้ '.. ' และ 'ใด ๆ '
- A
- ในฐานะ ' a ' แต่ยังแก้ไขการใช้ลิงก์สัญลักษณ์หากเป็นไปได้ โปรดทราบว่าความละเอียดของ '.. ' เกิดขึ้นก่อนการแก้ไขลิงก์สัญลักษณ์ การเรียกนี้เทียบเท่ากับ a เว้นแต่ว่าระบบของคุณมีการ
realpath
เรียกของระบบ (ระบบที่ทันสมัยทำ)- ชั่วโมง
- ลบคอมโพเนนต์ชื่อพา ธ ที่ต่อท้ายออกจากส่วนหัว ใช้งานได้เช่น '
dirname
'
เครดิต: Faux
บนสำหรับข้อเสนอแนะของการเริ่มต้นของการใช้#zsh
(../)#.git(:h)
(../)
กำลังทำอะไรอยู่ ... โอ้และใคร / คือFaux on #zsh
อะไร?
(../)
เพียงอย่างเดียวไม่ได้มีความหมายอะไรมาก แต่(../)#
หมายถึงพยายามขยายรูปแบบในวงเล็บ 0 หรือมากกว่านั้นและใช้เฉพาะรูปแบบที่มีอยู่จริงในระบบไฟล์ เนื่องจากเรากำลังขยายจากไดเรกทอรีหลัก 0 ถึง n เราจึงค้นหาขึ้นไปสู่รูทของระบบไฟล์ได้อย่างมีประสิทธิภาพ (หมายเหตุ: คุณอาจต้องการเพิ่มบางสิ่งหลังจากรูปแบบเพื่อให้มีความหมาย แต่สำหรับการดำเนินการตรัสรู้print -l (../)#
) และ#zsh
เป็นช่อง IRC Faux
เป็นชื่อผู้ใช้ในนั้น Faux
แนะนำวิธีการแก้ปัญหาดังนั้นเครดิต
(../)#.git(/Y1:a:h)
เพื่อหยุดที่พบครั้งแรกหนึ่ง (กับรุ่นล่าสุดของ zsh)
setopt extended_glob
การค้นหารุ่นนี้รองรับไวยากรณ์ "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
ฉันพบว่าการทำงานกับ 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 วิธีนี้แก้ปัญหานั้นได้
ฉันรู้ว่านี่เป็นสถานการณ์ที่เฉพาะเจาะจงมาก แต่ฉันคิดว่าทางออกมีค่า
โซลูชันของ 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"
}
ฉันแก้ไขโซลูชันของ 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