Réponses:
Dans GNU, find
vous pouvez utiliser des -printf
paramètres pour cela, par exemple:
find /dir1 -type f -printf "%f\n"
-o
a une priorité inférieure à celle implicite -a
, vous voudrez donc souvent regrouper vos -o
arguments)
Si votre recherche n'a pas d'option -printf, vous pouvez également utiliser le nom de base:
find ./dir1 -type f -exec basename {} \;
... {} ';'
Utilisation -execdir
qui contient automatiquement le fichier actuel {}
, par exemple:
find . -type f -execdir echo '{}' ';'
Vous pouvez également utiliser $PWD
au lieu de.
(sur certains systèmes, il ne produira pas de point supplémentaire à l'avant).
Si vous avez encore un point supplémentaire, vous pouvez également exécuter:
find . -type f -execdir basename '{}' ';'
-execdir utility [argument ...] ;
Le
-execdir
principal est identique au-exec
principal, à l'exception que l'utilitaire sera exécuté à partir du répertoire qui contient le fichier actuel .
Lorsqu'il est utilisé à la +
place de ;
, alors {}
est remplacé par autant de chemins que possible pour chaque invocation d'utilitaire. En d'autres termes, il imprimera tous les noms de fichiers sur une seule ligne.
./filename
au lieu de filename
. Selon vos besoins, cela peut ou peut ne pas être bien.
$PWD
au lieu de .
.
Si vous utilisez GNU find
find . -type f -printf "%f\n"
Ou vous pouvez utiliser un langage de programmation tel que Ruby (1.9+)
$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'
Si vous avez envie d'une solution bash (au moins 4)
shopt -s globstar
for file in **; do echo ${file##*/}; done
Si vous souhaitez exécuter une action uniquement contre le nom de fichier, utilisez basename
peut être difficile.
Par exemple ceci:
find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \;
fera simplement écho au nom de base /my/found/path
. Pas ce que nous voulons si nous voulons exécuter sur le nom de fichier.
Mais vous pouvez ensuite xargs
la sortie. par exemple pour tuer les fichiers dans un répertoire basé sur des noms dans un autre répertoire:
cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
-exec
et -execdir
sont lents, xargs
est roi.
$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l
139
0m01.17s real 0m00.20s user 0m00.93s system
139
0m01.16s real 0m00.20s user 0m00.92s system
139
0m01.05s real 0m00.17s user 0m00.85s system
139
0m00.93s real 0m00.17s user 0m00.85s system
139
0m00.88s real 0m00.12s user 0m00.75s system
xargs
Le parallélisme aide également.
Curieusement, je ne peux pas expliquer le dernier cas de xargs
sans-n1
. Il donne le résultat correct et c'est le plus rapide¯\_(ツ)_/¯
( basename
prend seulement 1 argument de chemin mais xargs
les enverra tous (en fait 5000) sans -n1
. ne fonctionne pas sur linux et openbsd, seulement macOS ...)
Quelques chiffres plus importants d'un système Linux pour voir comment cela -execdir
aide, mais toujours beaucoup plus lentement qu'un parallèle xargs
:
$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l
2358
3.63s real 0.10s user 0.41s system
2358
1.53s real 0.05s user 0.31s system
2358
1.30s real 0.03s user 0.21s system
2358
0.41s real 0.03s user 0.25s system
find
c'est -execdir
celui qui devient le plus rapide car la création de nouveaux processus est une opération relativement coûteuse.
Honnêtement basename
, les dirname
solutions sont plus faciles, mais vous pouvez également vérifier ceci:
find . -type f | grep -oP "[^/]*$"
ou
find . -type f | rev | cut -d '/' -f1 | rev
ou
find . -type f | sed "s/.*\///"
Comme d'autres l'ont souligné, vous pouvez combiner find
et basename
, mais par défaut, le basename
programme ne fonctionnera que sur un chemin à la fois, donc l'exécutable devra être lancé une fois pour chaque chemin (en utilisant l'un find ... -exec
ou l' autre find ... | xargs -n 1
), ce qui peut potentiellement être lent.
Si vous utilisez l' -a
option on basename
, elle peut accepter plusieurs noms de fichiers en une seule invocation, ce qui signifie que vous pouvez ensuite utiliser xargs
sans le -n 1
, pour regrouper les chemins d'accès en un nombre beaucoup plus faible d'invocations de basename
, ce qui devrait être plus efficace.
Exemple:
find /dir1 -type f -print0 | xargs -0 basename -a
Ici, j'ai inclus le -print0
et-0
(qui devrait être utilisé ensemble), afin de faire face à tout espace à l'intérieur des noms de fichiers et de répertoires.
Voici une comparaison temporelle, entre les xargs basename -a
xargs -n1 basename
versions et . (Par souci de comparaison, les synchronisations signalées ici sont après une exécution fictive initiale, afin qu'elles soient toutes les deux effectuées une fois que les métadonnées du fichier ont déjà été copiées dans le cache d'E / S.) J'ai canalisé la sortie vers cksum
dans les deux cas, juste pour démontrer que la sortie est indépendante de la méthode utilisée.
$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663
real 0m0.063s
user 0m0.058s
sys 0m0.040s
$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum'
2532163462 546663
real 0m14.504s
user 0m12.474s
sys 0m3.109s
Comme vous pouvez le voir, il est vraiment beaucoup plus rapide d'éviter de lancer à basename
chaque fois.
basename
acceptera plusieurs noms de fichiers sans avoir besoin d'arguments de ligne de commande supplémentaires. L'utilisation -a
ici est sous Linux. ( basename --version
me dit basename (GNU coreutils) 8.28
.)