AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 520078
Accepted
Brian Fitzpatrick
Brian Fitzpatrick
Asked: 2019-05-21 20:49:01 +0800 CST2019-05-21 20:49:01 +0800 CST 2019-05-21 20:49:01 +0800 CST

Localizando todos os arquivos com uma determinada extensão cujo nome base é o nome do diretório pai

  • 772

Quero procurar recursivamente todos os *.pdfarquivos em um diretório ~/foocujo nome base corresponda ao nome do diretório pai do arquivo.

Por exemplo, suponha que a estrutura de diretórios ~/foose pareça com isso

foo
├── dir1
│   ├── dir1.pdf
│   └── dir1.txt
├── dir2
│   ├── dir2.tex
│   └── spam
│       └── spam.pdf
└── dir3
    ├── dir3.pdf
    └── eggs
        └── eggs.pdf

A execução do meu comando desejado retornaria

~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf

Isso é possível usando findou algum outro utilitário principal? Suponho que isso seja possível usando a -regexopção para find, mas não tenho certeza de como escrever o padrão correto.

find
  • 6 6 respostas
  • 1344 Views

6 respostas

  • Voted
  1. Best Answer
    dedowsdi
    2019-05-21T21:16:53+08:002019-05-21T21:16:53+08:00

    Com GNU find:

    find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
    
    • -regextype egrepuse regex estilo egrep.
    • .*/corresponder às diretrizes dos avós.
    • ([^/]+)/corresponder ao diretório pai em um grupo.
    • \1\.pdfuse backreferencepara corresponder ao nome do arquivo como diretório pai.

    atualizar

    Um (eu para um) pode pensar que .*é ganancioso o suficiente, é desnecessário excluir /da correspondência dos pais:

    find . -regextype egrep -regex '.*/(.+)/\1\.pdf'
    

    O comando acima não funcionará bem, porque corresponde a ./a/b/a/b.pdf:

    • .*/fósforos./
    • (.+)/fósforosa/b/
    • \1.pdffósforosa/b.pdf
    • 16
  2. Inian
    2019-05-21T21:49:26+08:002019-05-21T21:49:26+08:00

    A variante de loop tradicional find .. -exec sh -c ''para usar as construções do shell para corresponder ao nome base e o caminho imediato acima seria fazer abaixo.

    find foo/ -name '*.pdf' -exec sh -c '
        for file; do 
            base="${file##*/}"
            path="${file%/*}"
            if [ "${path##*/}" =  "${base%.*}" ]; then
                printf "%s\n" "$file" 
            fi
        done' sh {} +
    

    Para dividir as expansões de parâmetros individuais

    • filecontém o caminho completo do .pdfarquivo retornado do findcomando
    • "${file##*/}"contém apenas a parte a seguir à última /ie apenas o nome base do ficheiro
    • "${file%/*}"contém o caminho até ao final /ie excepto a parte do nome base do resultado
    • "${path##*/}"contém a parte após a última /da pathvariável, ou seja, o caminho imediato da pasta acima do nome base do arquivo
    • "${base%.*}"contém a parte do nome base com a .pdfextensão removida

    Portanto, se o nome base sem extensão corresponder ao nome da pasta imediata acima, imprimimos o caminho.

    • 7
  3. Kusalananda
    2019-05-21T22:58:58+08:002019-05-21T22:58:58+08:00

    O inverso da resposta de Inian , ou seja, procure diretórios e veja se eles mantêm um arquivo com um nome específico.

    O seguinte imprime os nomes de caminho dos arquivos encontrados relativos ao diretório foo:

    find foo -type d -exec sh -c '
        for dirpath do
            pathname="$dirpath/${dirpath##*/}.pdf"
            if [ -f "$pathname" ]; then
                printf "%s\n" "$pathname"
            fi
        done' sh {} +
    

    ${dirpath##*/}será substituído pela parte do nome do arquivo do caminho do diretório e pode ser substituído por $(basename "$dirpath").

    Para pessoas que gostam da sintaxe de curto-circuito:

    find foo -type d -exec sh -c '
        for dirpath do
            pathname="$dirpath/${dirpath##*/}.pdf"
            [ -f "$pathname" ] && printf "%s\n" "$pathname"
        done' sh {} +
    

    A vantagem de fazer isso dessa maneira é que você pode ter mais arquivos PDF do que diretórios. O número de testes envolvidos é reduzido se restringirmos a consulta pelo número menor (o número de diretórios).

    Por exemplo, se um único diretório contiver 100 arquivos PDF, isso tentará detectar apenas um deles, em vez de testar os nomes de todos os 100 arquivos com os do diretório.

    • 7
  4. Stéphane Chazelas
    2019-05-21T21:59:01+08:002019-05-21T21:59:01+08:00

    com zsh:

    printf '%s\n' **/*/*.pdf(e@'[[ $REPLY:t = $REPLY:h:t.pdf ]]'@)
    

    Cuidado que while **/não seguirá links simbólicos, */seguirá.

    • 3
  5. user1717828
    2019-05-22T07:58:13+08:002019-05-22T07:58:13+08:00

    Não foi especificado, mas aqui está uma solução sem expressões regulares se alguém estiver interessado.

    Podemos usar find . -type fapenas para obter arquivos, depois utilizar dirnamee basenameescrever o condicional. Os utilitários têm o seguinte comportamento:

    $ find . -type f
    ./dir2/spam/spam.pdf
    ./dir2/dir2.tex
    ./dir3/dir3.pdf
    ./dir3/eggs/eggs.pdf
    ./dir1/dir1.pdf
    ./dir1/dir1.txt
    

    basenameretorna apenas o nome do arquivo após o último /:

    $ for file in $(find . -type f); do basename $file; done
    spam.pdf
    dir2.tex
    dir3.pdf
    eggs.pdf
    dir1.pdf
    dir1.txt
    

    dirnamedá todo o caminho até o final /:

    $ for file in $(find . -type f); do dirname $file; done
    ./dir2/spam
    ./dir2
    ./dir3
    ./dir3/eggs
    ./dir1
    ./dir1
    

    Portanto, basename $(dirname $file)fornece o diretório pai do arquivo.

    $ for file in $(find . -type f); do basename $(dirname $file) ; done
    spam
    dir2
    dir3
    eggs
    dir1
    dir1
    

    Solução

    Combine o acima para formar a condicional "$(basename $file)" = "$(basename $(dirname $file))".pdfe, em seguida, imprima apenas cada resultado findse essa condicional retornar true.

    $ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
    ./dir2/spam/spam.pdf
    ./dir3/dir3.pdf
    ./dir3/eggs/eggs.pdf
    ./dir1/dir1.pdf
    ./Final Thesis/grits/grits.pdf
    ./Final Thesis/Final Thesis.pdf
    

    No exemplo acima, adicionamos um diretório/arquivo com espaços no nome para tratar esse caso (graças a @Kusalananda nos comentários)

    • 2
  6. docgyneco69
    2019-05-24T18:34:06+08:002019-05-24T18:34:06+08:00

    Eu tomo bash globbing, loop simples sobre testes de string a qualquer dia no programa Find . Chame-me de irracional e, embora possa ser subótimo, esse código simples faz o truque para mim: legível e reutilizável, até satisfatório!. Permitam-me, portanto, sugerir uma combinação de:

    • bash globstar : for f in ** ; do ... ** faz um loop sobre todos os arquivos no diretório atual e todas as subpastas.. para verificar o status do globstar em sua sessão atual: shopt -p globstar. Para ativar o globstar: shopt -s globstar.

    • utilitário "arquivo" : if [[ $(file "$f") =~ pdf ]]; then ... para verificar o formato real do arquivo para pdf - mais robusto do que testar apenas a extensão do arquivo

    • basename, dirname : para comparar o nome do arquivo com o nome do diretório imediatamente acima dele. basenameretorna o nome do arquivo - dirnameretorna o caminho do diretório inteiro - combina as duas funções para retornar apenas o diretório que contém o arquivo correspondente. Eu coloco cada um em uma variável ( _mydir e _myf ) para fazer um teste simples usando =~ para correspondência de strings.

    Uma sutileza: remova qualquer "ponto" no nome do arquivo para evitar a correspondência do nome do arquivo com o diretório atual cujo atalho também é "." - Usei substituição direta de strings na variável _myf : ${_myf//./}- não é muito elegante, mas funciona. As correspondências positivas retornarão o caminho de cada arquivo - junto com o caminho completo da pasta atual, precedendo a saída com : $(pwd)/.

    Código

    for f in ** ; do
      if [[ $(file "$f") =~ PDF ]]; then
        _mydir="$(basename $(dirname $f))" ; 
        _myf="$(basename $f)" ; 
        [[ "${_myf//./}" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ; 
      fi ; 
    done
    
    • 0

relate perguntas

  • Comportamento estranho para localizar se o padrão seguinte -name não estiver entre aspas

  • Imprima variáveis ​​do subshell para o shell pai [duplicado]

  • verificando se existe uma pasta em determinado diretório

  • Localizar e substituir cores em arquivos CSS na linha de comando

  • GNU find: obtenha caminho absoluto e relativo em -exec

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve