[HS] un filtre 'find alike'

2 réponses
Avatar
Marc Chantreux
salut à tous,

j'aimerais beaucoup pouvoir utiliser find pour filtrer une liste de
chemins selon des critères de recherche. par exemple:

dpkg -L vim | find - -type f

ça n'est malheureusement pas possible. il existe de multiples
alternatives

une commande | while read p; do
test -f $p && echo $p
done

mais c'est chiant à écrire et pas super efficace sur de grosses listes

une commande | xargs -I% find % -prune -type f

qui va lancer 1 find par ligne :(

une commande | xargs -J% find % -prune -type f

qui marche sur les *BSD mais pas avec GNU xargs

et finalement ma préférée sous linux

une commande | perl -lne 'print if -f'

qui me fait degainer perl pour un truc aussi simple.

est ce que quelqu'un aurait une solution simple, efficace et qui marche
sous linux?

cordialement,
marc

2 réponses

Avatar
Francois Lafont
Bonjour,
On 6/30/19 12:03 AM, Marc Chantreux wrote:
j'aimerais beaucoup pouvoir utiliser find pour filtrer une liste de
chemins selon des critères de recherche. par exemple:
dpkg -L vim | find - -type f
ça n'est malheureusement pas possible. il existe de multiples
alternatives
une commande | while read p; do
test -f $p && echo $p
done
mais c'est chiant à écrire

Je te l'accorde...
et pas super efficace sur de grosses listes

... tu es sûr de ça ? J'ai un doute, parce que "while" est un mot clé,
"read" et "test" sont built-in. Du coup, sauf erreur, il n'y a pas de
fork sur la partie droite du pipe et j'aurais donc dis que ça doit être
justement plutôt efficace sur de grosses listes. Je me trompe ?
Personnellement, c'est la commande que j'aurais utilisée.
--
François Lafont
Avatar
Marc Chantreux
hello,
une commande | while read p; do
test -f $p && echo $p
done

mais c'est chiant à écrire
Je te l'accorde...

bon réellement j'ai dans mon .zshenv:
'?-' () while {read it} {"$@" $it && print $it}
du coup en vrai j'écris
une commande |?- test -f
mais si j'écris un script pour les collègues, je me force à ne pas
utiliser ce genre de shortcuts
et pas super efficace sur de grosses listes

... tu es sûr de ça ? J'ai un doute, parce que "while" est un mot clé,
"read" et "test" sont built-in.

d'experience, il n'y a pas photo: les filtres sont toujours plus
performants que les while loops.
d'un autre coté: j'ai acquis cette conviction il y a fort longtemps
(fin du millénaire dernier ) sans actualiser ni approffondir ...
alors j'ai fais un benchmark au doigt mouillé (le script ci-apres) qui
me permet de confirmer ...
sachant que:
* bash n'est pas présent dans le test parceque:
* c'est un shell que je n'utilise pas
* j'ai fais le test sur une box openbsd pour avoir xargs -J
* je n'ai même pas essayé avec xargs -I ... ce sera forcément tristoune
* zsh fait un bien mauvais score: je note mais ca n'est pas le sujet
alors je n'ai pas cherché à savoir si c'était perfectible
* perl forever o/
shell user kernel elapsed
ksh 0.75s 0.57s 1.33s
zsh 2.77s 4.97s 7.73s
sh 0.80s 0.53s 1.33s
xargs 0.13s 0.61s 0.74s
perl 0.07s 0.37s 0.44s
le script en question:
# creer un jeu de données
find . > sep---
cat sep---{,,,,,,,,,,,} > sep-0a
tr 'n' '' < sep-0a > sep-00
times=5
# lire le jeu une premiere fois histoire d'etre fair play
cat sep* > /dev/null
# while loop
script='
while read it; do
test -f "$it" && echo "$it"
done > /dev/null
'
print shell user kernel elapsed
for shell (ksh zsh sh) {
TIMEFMT="$shell %U %S %E"
< sep-0a time $shell -c $script
}
TIMEFMT="xargs %U %S %E"
< sep-00 time sh -c 'xargs -0 -J % find % -prune -type f > /dev/null 2>&1'
TIMEFMT="perl %U %S %E"
< sep-0a time sh -c ' perl -lne"print if -f" > /dev/null 2>&1 '