Je suis imbriqué profondément dans un arbre de fichiers et j'aimerais trouver quel répertoire parent contient un fichier.
Par exemple. Je suis dans un ensemble de référentiels git imbriqués et je veux trouver le .git
Répertoire contrôlant les fichiers que je suis actuellement. J'espère que quelque chose comme
find -searchup -iname ".git"
Une version encore plus générale qui permet d'utiliser find
options:
#!/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
Par exemple (en supposant que le script soit enregistré comme find_up.sh
)
find_up.sh some_dir -iname "foo*bar" -execdir pwd \;
... Imprimer les noms de tous les ancêtres de some_dir
(y compris lui-même) jusqu'à /
dans lequel un fichier avec le motif est trouvé.
Lorsque vous utilisez readlink -f
Le script ci-dessus suivra des liens symboliques sur le chemin, comme indiqué dans les commentaires. Vous pouvez utiliser realpath -s
Au lieu de cela, si vous souhaitez suivre les chemins par nom ("/ FOO/BAR" ira à "FOO" Même si "bar" est un lien symbolique) - Toutefois, qui nécessite une installation realpath
qui n'est pas installé par défaut sur la plupart des plates-formes.
git rev-parse --show-toplevel
imprimera le répertoire de niveau supérieur du référentiel actuel, si vous êtes en une.
Autres options connexes:
# `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
Une version généralisée de la réponse des Gilles, premier paramètre utilisé pour trouver le match:
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
Conserve l'utilisation de liaisons symmètres.
Trouver ne peut pas le faire. Je ne peux penser à rien plus simple qu'une boucle de coquille. (Non testé, suppose qu'il n'y a pas /.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
Pour le cas spécifique d'un référentiel GIT, vous pouvez laisser Git faire le travail pour vous.
git_root=$(GIT_EDITOR=echo git config -e)
git_root=${git_root%/*}
Si vous utilisez ZSH avec un globbing étendu activé, vous pouvez le faire avec un ONLININ:
(../)#.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'
Explication (citée de man zshexpn
):
Globbing récursif
Un composant de chemin de chemin du formulaire
(foo/)#
correspond à un chemin constitué de zéro ou plusieurs répertoires correspondant au modèle FOO. Comme un raccourci,**/
est équivalent à(*/)#
.modificateurs
Après le mot optionnel désignateur, vous pouvez ajouter une séquence d'un ou de plusieurs des modificateurs suivants, chacun précédé d'un ":". Ces modificateurs fonctionnent également à la suite d'une génération de nom de fichier et d'une expansion des paramètres, sauf indication contraire.
- A
- Tournez un nom de fichier en un chemin absolu: Rependez le répertoire actuel, si nécessaire, et résout toute utilisation de '..' et '.' '
- [~ # ~] A [~ # ~ ~]
- Comme ' A', mais également résoudre l'utilisation de liens symboliques dans la mesure du possible. Notez que la résolution de '..' se produit avant la résolution des liens symboliques. Cet appel est équivalent à un si votre système dispose de l'appel système
realpath
(systèmes modernes do).- H
- Supprimez un composant de chemin de fuite, laissant la tête. Cela fonctionne comme '
dirname
'.
Crédits: Faux
sur #zsh
Pour la suggestion initiale d'utiliser (../)#.git(:h)
.
J'ai découvert que travailler avec des liens sympathiques tuer certaines des autres options. Surtout les réponses spécifiques à GIT. J'ai à mi-chemin créé mon propre préféré de - ceci réponse originale qui est assez efficace.
#!/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"
}
J'utilise des liens Symlinks pour mes projets Go, car vous voulez obtenir un code source dans un endroit donné et j'aime garder mes projets sous ~/Projets. Je crée le projet en $ GOPATH/SRC et les lymniques à ~/Projets. Alors courir git rev-parse --show-toplevel
Imprime le répertoire $ GOPATH, pas le répertoire ~/projets. Cette solution résout ce problème.
Je me rends compte que c'est une situation très précise, mais je pense que la solution est précieuse.
Récursion peut entraîner une solution assez concise.
#!/usr/bin/env bash
parent-find() {
local file="$1"
local dir="$2"
test -e "$dir/$file" && echo "$dir" && return 0
[ '/' = "$dir" ] && return 1
parent-find "$file" "$(dirname "$dir")"
}
# Example
parent-find .bashrc "/home/user/projects/parent-find"
# should respond with "/home/user"
Vincent Scheib's solution ne fonctionne pas pour les fichiers qui résident dans le répertoire racine.
La version suivante fait et vous permet également de passer le directeur de départ comme le 1er argument.
find-up() {
path="$(realpath -s "$1")"
while ! [ -e "$path"/"$2" ] && [ -n "$path" ]; do
path="${path%/*}"
done
[ -e "$path"/"$2" ] && echo "$path"/"$2"
}
Cette version de FINDUP prend en charge la syntaxe "Rechercher", comme la réponse de @ Sinelaw, mais également en charge les symboles sans avoir besoin de realpath. Il prend également en charge une fonctionnalité "Arrêt à" optionnelle ", de sorte que cela fonctionne: findup .:~ -name foo
... cherche FOO sans passer le direct 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
J'ai modifié Solution de Sinelaw Pour être POSIX. Il ne suit pas les symboles liés et comprend la recherche du répertoire racine à l'aide d'une boucle.
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