Je veux rechercher récursivement chaque *.pdf
fichier dans un répertoire ~/foo
dont le nom de base correspond au nom du répertoire parent du fichier.
Par exemple, supposons que la structure du répertoire ~/foo
ressemble à ceci
foo
├── dir1
│ ├── dir1.pdf
│ └── dir1.txt
├── dir2
│ ├── dir2.tex
│ └── spam
│ └── spam.pdf
└── dir3
├── dir3.pdf
└── eggs
└── eggs.pdf
L'exécution de ma commande souhaitée reviendrait
~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf
Est-ce possible d'utiliser find
ou un autre utilitaire de base? Je suppose que cela est faisable en utilisant l' -regex
option pour find
mais je ne sais pas comment écrire le bon modèle.
Réponses:
Avec GNU
find
:-regextype egrep
utilisez l'expression rationnelle de style egrep..*/
correspondre aux directires du grand parent.([^/]+)/
correspond au répertoire parent dans un groupe.\1\.pdf
utiliserbackreference
pour faire correspondre le nom du fichier au répertoire parent.mise à jour
On (moi-même pour un) pourrait penser que
.*
c'est assez gourmand, il n'est pas nécessaire d'exclure/
de la correspondance des parents:La commande ci-dessus ne fonctionnera pas bien, car elle corrige
./a/b/a/b.pdf
:.*/
allumettes./
(.+)/
allumettesa/b/
\1.pdf
allumettesa/b.pdf
la source
find . -regex '.*/\([^/]*\)/\1\.pdf'
alors cela fonctionnerait même avec BSDfind
.La variante de boucle traditionnelle du
find .. -exec sh -c ''
pour utiliser les constructions shell pour faire correspondre le nom de base et le chemin immédiat ci-dessus serait de faire ci-dessous.Pour décomposer les extensions de paramètres individuels
file
contient le chemin complet du.pdf
fichier renvoyé par lafind
commande"${file##*/}"
contient uniquement la partie après la dernière,/
c'est-à-dire uniquement le nom de base du fichier"${file%/*}"
contient le chemin jusqu'à la finale,/
c'est-à-dire à l'exception de la partie du nom de base du résultat"${path##*/}"
contient la partie après la dernière/
de lapath
variable, c'est-à-dire le chemin du dossier immédiat au-dessus du nom de base du fichier"${base%.*}"
contient la partie du nom de base avec l'.pdf
extension suppriméeDonc, si le nom de base sans extension correspond au nom du dossier immédiat ci-dessus, nous imprimons le chemin.
la source
L'inverse de la réponse d' Inian , c'est-à-dire rechercher des répertoires, puis voir s'ils contiennent un fichier avec un nom particulier.
Les informations suivantes affichent les chemins d'accès des fichiers trouvés par rapport au répertoire
foo
:${dirpath##*/}
sera remplacé par la partie nom de fichier du chemin du répertoire et pourrait être remplacé par$(basename "$dirpath")
.Pour les personnes qui aiment la syntaxe de court-circuit:
L'avantage de procéder de cette façon est que vous pouvez avoir plus de fichiers PDF que de répertoires. Le nombre de tests impliqués est réduit si l'on restreint la requête par le plus petit nombre (le nombre de répertoires).
Par exemple, si un seul répertoire contient 100 fichiers PDF, cela n'essaierait que de détecter l'un d'entre eux plutôt que de tester les noms des 100 fichiers par rapport à celui du répertoire.
la source
avec
zsh
:Attention, bien que
**/
ne suivra pas les liens symboliques, le*/
fera.la source
Cela n'a pas été spécifié, mais voici une solution sans expressions régulières si quelqu'un est intéressé.
Nous pouvons utiliser
find . -type f
pour obtenir simplement des fichiers, puis utiliserdirname
etbasename
écrire le conditionnel. Les utilitaires ont le comportement suivant:basename
renvoie juste le nom de fichier après le dernier/
:dirname
donne le chemin complet jusqu'à la finale/
:Par conséquent,
basename $(dirname $file)
donne le répertoire parent du fichier.Solution
Combinez ce qui précède pour former le conditionnel
"$(basename $file)" = "$(basename $(dirname $file))".pdf
, puis n'imprimez chaque résultatfind
que si ce conditionnel renvoie true.Dans l'exemple ci-dessus, nous avons ajouté un répertoire / fichier avec des espaces dans le nom pour traiter ce cas (merci à @Kusalananda dans les commentaires)
la source
Final Thesis.pdf
(avec un espace).Je prends bash globbing, simple boucle sur des tests de chaîne tous les jours sur le programme Find . Appelez-moi irrationnel, et bien qu'il puisse être sous-optimal, ce code simple fait l'affaire pour moi: lisible et réutilisable, satisfaisant même!. Permettez-moi donc de suggérer une combinaison de:
• bash globstar :
for f in ** ; do ...
** boucle sur tous les fichiers dans le répertoire courant et tous les sous - dossiers .. pour vérifier l' état de globstar dans votre session en cours:shopt -p globstar
. Pour activer globstar:shopt -s globstar
.• utilité "fichier" :
if [[ $(file "$f") =~ pdf ]]; then ...
pour vérifier le format de fichier réel pour pdf - plus robuste que de tester uniquement l'extension du fichier• basename, dirname : pour comparer le nom du fichier au nom du répertoire immédiatement au-dessus.
basename
retourne le nom du fichier -dirname
retourne le chemin complet du répertoire - combinez les deux fonctions pour ne retourner que le seul répertoire contenant le fichier correspondant. Je mets chacun dans une variable ( _mydir et _myf ) pour ensuite faire un test simple en utilisant = ~ pour la correspondance de chaîne.Une subtilité: supprimez tout "point" dans le nom de fichier pour éviter de faire correspondre le nom de fichier au répertoire actuel dont le raccourci est également "." - J'ai utilisé la substitution de chaîne directe sur la variable _myf :
${_myf//./}
- pas très élégante mais ça marche. Matchs positifs retourneront le chemin de chaque fichier - avec le chemin complet du dossier en cours en précédant la sortie avec:$(pwd)/
.Code
la source