Quero procurar recursivamente todos os *.pdf
arquivos em um diretório ~/foo
cujo nome base corresponda ao nome do diretório pai do arquivo.
Por exemplo, suponha que a estrutura de diretórios ~/foo
se 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 find
ou algum outro utilitário principal? Suponho que isso seja possível usando a -regex
opção para find
, mas não tenho certeza de como escrever o padrão correto.
Com GNU
find
:-regextype egrep
use regex estilo egrep..*/
corresponder às diretrizes dos avós.([^/]+)/
corresponder ao diretório pai em um grupo.\1\.pdf
usebackreference
para 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:O comando acima não funcionará bem, porque corresponde a
./a/b/a/b.pdf
:.*/
fósforos./
(.+)/
fósforosa/b/
\1.pdf
fósforosa/b.pdf
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.Para dividir as expansões de parâmetros individuais
file
contém o caminho completo do.pdf
arquivo retornado dofind
comando"${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/
dapath
variável, ou seja, o caminho imediato da pasta acima do nome base do arquivo"${base%.*}"
contém a parte do nome base com a.pdf
extensão removidaPortanto, se o nome base sem extensão corresponder ao nome da pasta imediata acima, imprimimos o caminho.
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
:${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:
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.
com
zsh
:Cuidado que while
**/
não seguirá links simbólicos,*/
seguirá.Não foi especificado, mas aqui está uma solução sem expressões regulares se alguém estiver interessado.
Podemos usar
find . -type f
apenas para obter arquivos, depois utilizardirname
ebasename
escrever o condicional. Os utilitários têm o seguinte comportamento:basename
retorna apenas o nome do arquivo após o último/
:dirname
dá todo o caminho até o final/
:Portanto,
basename $(dirname $file)
fornece o diretório pai do arquivo.Solução
Combine o acima para formar a condicional
"$(basename $file)" = "$(basename $(dirname $file))".pdf
e, em seguida, imprima apenas cada resultadofind
se essa condicional retornar true.No exemplo acima, adicionamos um diretório/arquivo com espaços no nome para tratar esse caso (graças a @Kusalananda nos comentários)
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.
basename
retorna o nome do arquivo -dirname
retorna 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