Quiero buscar recursivamente cada *.pdf
archivo en un directorio ~/foo
cuyo nombre base coincida con el nombre del directorio principal del archivo.
Por ejemplo, suponga que la estructura del directorio se ~/foo
ve así
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
Ejecutar mi comando deseado volvería
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
¿Es esto posible usar find
o alguna otra utilidad básica? Supongo que esto es factible usando la -regex
opción find
pero no estoy seguro de cómo escribir el patrón correcto.
Respuestas:
Con GNU
find
:-regextype egrep
use la expresión regular de estilo egrep..*/
coincidir con las directivas de los abuelos.([^/]+)/
hacer coincidir el directorio padre en un grupo.\1\.pdf
usebackreference
para hacer coincidir el nombre del archivo como directorio principal.actualizar
Uno (yo mismo) podría pensar que
.*
es lo suficientemente codicioso, no es necesario excluir/
de la coincidencia de padres:El comando anterior no funcionará bien, porque coincide
./a/b/a/b.pdf
:.*/
partidos./
(.+)/
partidosa/b/
\1.pdf
partidosa/b.pdf
fuente
find . -regex '.*/\([^/]*\)/\1\.pdf'
y luego incluso funcionaría con BSDfind
.La variante de bucle tradicional de
find .. -exec sh -c ''
usar las construcciones de shell para que coincida con el nombre base y la ruta inmediata anterior sería hacer a continuación.Para desglosar las expansiones de parámetros individuales
file
contiene la ruta completa del.pdf
archivo devuelto por elfind
comando"${file##*/}"
contiene solo la parte posterior a la última,/
es decir, solo el nombre base del archivo"${file%/*}"
contiene la ruta hasta el final,/
es decir, excepto la parte del nombre base del resultado"${path##*/}"
contiene la parte posterior a la última/
de lapath
variable, es decir, la ruta de la carpeta inmediata sobre el nombre base del archivo"${base%.*}"
contiene la parte del nombre base con la.pdf
extensión eliminadaEntonces, si el nombre base sin extensión coincide con el nombre de la carpeta inmediata anterior, imprimimos la ruta.
fuente
El reverso de la respuesta de Inian , es decir, buscar directorios, y luego ver si contienen un archivo con un nombre en particular.
A continuación se imprimen los nombres de ruta de los archivos encontrados en relación con el directorio
foo
:${dirpath##*/}
será reemplazado por la parte del nombre del archivo de la ruta del directorio, y podría ser reemplazado por$(basename "$dirpath")
.Para las personas que les gusta la sintaxis de cortocircuito:
El beneficio de hacerlo de esta manera es que puede tener más archivos PDF que directorios. El número de pruebas involucradas se reduce si uno restringe la consulta por un número menor (el número de directorios).
Por ejemplo, si un solo directorio contiene 100 archivos PDF, esto solo intentaría detectar uno de ellos en lugar de probar los nombres de los 100 archivos con respecto al del directorio.
fuente
con
zsh
:Tenga en cuenta que si bien
**/
no seguirá enlaces simbólicos, lo*/
hará.fuente
No se especificó, pero aquí hay una solución sin expresiones regulares si alguien está interesado.
Podemos usar
find . -type f
para obtener archivos, luego utilizardirname
ybasename
escribir el condicional. Las utilidades tienen el siguiente comportamiento:basename
devuelve solo el nombre del archivo después del último/
:dirname
da todo el camino hasta la final/
:Por lo tanto,
basename $(dirname $file)
proporciona el directorio principal del archivo.Solución
Combine lo anterior para formar el condicional
"$(basename $file)" = "$(basename $(dirname $file))".pdf
, luego solo imprima cada resultadofind
si ese condicional devuelve verdadero.En el ejemplo anterior, hemos agregado un directorio / archivo con espacios en el nombre para tratar ese caso (gracias a @Kusalananda en los comentarios)
fuente
Final Thesis.pdf
(con un espacio).Tomo bash globbing, pruebas simples de bucle sobre cadena cualquier día sobre el programa Find . Llámame irracional, y si bien puede ser subóptimo, un código tan simple me sirve: ¡legible y reutilizable, incluso satisfactorio! Permítanme, por lo tanto, sugerir una combinación de:
• fiesta Globstar :
for f in ** ; do ...
** bucles más de todos los archivos en el directorio actual y todas las subcarpetas .. comprobar el estado de Globstar en su sesión actual:shopt -p globstar
. Para activar Globstar:shopt -s globstar
.• Utilidad "archivo" :
if [[ $(file "$f") =~ pdf ]]; then ...
para verificar el formato de archivo real para pdf , más robusto que probar solo para la extensión del archivo• basename, dirname : para comparar el nombre del archivo con el nombre del directorio inmediatamente superior.
basename
devuelve el nombre del archivo -dirname
devuelve la ruta completa del directorio - combina las dos funciones para devolver solo el directorio que contiene el archivo correspondiente. Puse cada uno en una variable ( _mydir y _myf ) para luego hacer una prueba simple usando = ~ para la coincidencia de cadenas.Una sutileza: elimine cualquier "punto" en el nombre del archivo para evitar que coincida con el directorio actual cuyo acceso directo también es "." - Utilicé la sustitución directa de cadenas en la variable _myf :
${_myf//./}
- no es muy elegante pero funciona. Coincidencias positivas volverán ruta de cada archivo - junto con la ruta completa de la carpeta actual precediendo la salida con:$(pwd)/
.Código
fuente