web-dev-qa-db-fra.com

Trouvez la recherche dans les annuaires parents au lieu de sous-répertoires

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"
46
Vincent Scheib

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.

16
sinelaw
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
30
ephemient

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.

20
Vincent Scheib

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).

12
unthought

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.

0
blockloop

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"
0
bas080

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"
}
0
Fabio A.

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
0
Erik Aronesty

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
0
dosentmatter