encontra procura em diretórios pai em vez de subdiretórios

40

Estou aninhado em uma árvore de arquivos e gostaria de descobrir qual diretório pai contém um arquivo.

Por exemplo Eu estou em um conjunto de repositórios Git aninhados e quero encontrar o diretório .git controlando os arquivos que estou atualmente. Eu espero por algo como find -searchup -iname ".git"

    
por Vincent Scheib 26.01.2011 / 01:48

8 respostas

13

Uma versão ainda mais geral que permite usar as opções 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

Por exemplo (supondo que o script seja salvo como find_up.sh )

find_up.sh some_dir -iname "foo*bar" -execdir pwd \;

... imprime os nomes de todos os antepassados de some_dir (incluindo ele próprio) até / no qual um arquivo com o padrão é encontrado.

Ao usar readlink -f , o script acima seguirá os links simbólicos na subida, conforme observado nos comentários. Você pode usar realpath -s , se você quiser seguir os caminhos pelo nome ("/ foo / bar" irá para "foo" mesmo que "bar" seja um link simbólico) - no entanto, isso requer a instalação de realpath , que não é não é instalado por padrão na maioria das plataformas.

    
por 28.03.2012 / 21:10
24
git rev-parse --show-toplevel

imprimirá o diretório de nível superior do repositório atual, se você estiver em um.

Outras opções relacionadas:

# '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
    
por 26.01.2011 / 05:54
15

Encontrar não pode fazer isso. Não consigo pensar em nada mais simples que um loop de shell. (Não testado, assume que não há /.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

Para o caso específico de um repositório git, você pode deixar o git fazer o trabalho para você.

git_root=$(GIT_EDITOR=echo git config -e)
git_root=${git_root%/*}
    
por 26.01.2011 / 02:01
13

Uma versão generalizada da resposta de Gilles, primeiro parâmetro usado para encontrar correspondência:

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

Mantém o uso de links sym.

    
por 07.10.2011 / 03:02
10

Se você estiver usando o zsh com a extensão de globbing ativada, você poderá fazê-lo com um 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'

Explicação (citada de man zshexpn ):

Recursive Globbing

A pathname component of the form (foo/)# matches a path consisting of zero or more directories matching the pattern foo. As a shorthand, **/ is equivalent to (*/)#.

Modifiers

After the optional word designator, you can add a sequence of one or more of the following modifiers, each preceded by a ':'. These modifiers also work on the result of filename generation and parameter expansion, except where noted.

  • a
    • Turn a file name into an absolute path: prepends the current directory, if necessary, and resolves any use of '..' and '.'
  • A
    • As 'a', but also resolve use of symbolic links where possible. Note that resolution of '..' occurs before resolution of symbolic links. This call is equivalent to a unless your system has the realpath system call (modern systems do).
  • h
    • Remove a trailing pathname component, leaving the head. This works like 'dirname'.

Créditos: Faux on #zsh para a sugestão inicial de usar (../)#.git(:h) .

    
por 08.02.2013 / 12:22
0

Esta versão do findup suporta a sintaxe "find", como a resposta do @ sinelaw, mas também suporta symlinks sem precisar do realpath. Ele também suporta um recurso opcional "stop at", então isso funciona: findup .:~ -name foo ... procura por foo sem passar o diretório home.

#!/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
    
por 08.06.2015 / 22:24
0

Descobri que trabalhar com links simbólicos elimina algumas das outras opções. Especialmente as respostas específicas do git. Eu já criei meu próprio favorito a partir de esta resposta original que é bastante eficiente.

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

Estou usando links simbólicos para meus projetos porque quero obter código-fonte em um determinado local e gosto de manter meus projetos em ~ / projetos. Eu crio o projeto em $ GOPATH / src e o link simbólico para ~ / projects. Portanto, a execução de git rev-parse --show-toplevel imprime o diretório $ GOPATH, não o diretório ~ / projects. Esta solução resolve esse problema.

Eu percebo que esta é uma situação muito específica, mas acho que a solução é valiosa.

    
por 14.07.2015 / 05:48
0

solução de Vincent Scheib não funciona para arquivos que residem no diretório raiz.

A versão a seguir também permite que você passe o diretório inicial como o primeiro argumento.

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

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

    [ -e "$path"/"$2" ] && echo "$path"/"$2"
}
    
por 05.10.2017 / 23:23

Tags