Observations sur grep
grep est ou outil de recherche bien optionné, sous-exploité lorsqu’utilisé sporadiquement, qui a l’avantage d’être disponible par défaut, mais qui présente certains inconvénients:
● sa récursivité est non maîtrisable; -r est juste binaire, ce qui est problématique dans de nombreux cas;
● la sortie non formatée par défaut des résultats rend difficile l’exploitation des recherches lorsque le nombre de fichiers est élevé;
● ses options par défaut ne sont pas les plus judicieuces en usage courant;
En résumé, grep n’est pas adapté en usage terminal pour une recherche avancée dans une arborescence de répertoires.
Il existe de nombreuses solutions de recherche de contenu, mais n’en trouvant aucune répondant à mes critères d’usage courant, fgp est né (fgp = contraction de find + grep).
fgp, c’est quoi ?
C’est un outil de recherche de contenu fichiers à récursivité adaptative s’appuyant sur find et grep, pour usage terminal, offrant une sortie conviviale des résultats.
Son usage reste simple, intuitif, en limitant l’accès aux options essentielles et courantes.
/!\ → non adapté pour usage dans un script.
Pourquoi une récursivité adaptative ?
Parce-que généralement, lorsqu’on choisit un répertoire initial de recherche, on est en priorité plus intéressé par ce qui sera trouvé aux niveaux 1,2,3 qu’au niveau 15 de sous-répertoires.
Usage:
$ fgp <options> <regex Etendu> <fichier ou rep> <fichier ou rep> ...
Options par défaut:
● recherche à partir du répertoire courant, si aucun répertoire indiqué;
● récursivité automatique (max 10) jusqu’aux premiers résultats, ou niveau max imposé par option ‹ -N ›
● regex Etendu ; ex: ‹ (abc|xyz) ›
● Masjucules/minuscules indifférenciées (inversable avec -ni)
● ne traverse pas les systèmes de fichiers différents
● exclusion des répertoires/fichiers cachés
● exclusion des répertoires .cache, Trash, lost+found, et fichiers binaires
● exclusion des fichiers non autorisés
● avertissement au delà de 100 fichiers trouvés
● extraction partielle ligne pour les très longues lignes, pour ne pas saturer l’affichage
● affichage du temps de recherche (si > 1 sec)
● détection des variables commençant par un tiret
Options:
-N : profondeur max N de recherche répertoires
-r : récursivité (max 10)
-mN : affichage limité aux N premières lignes trouvées par fichier (ex: -m2)
-a,--all : recherche aussi dans les fichiers cachés
-l,--files-with-matches : seul le nom du fichier sera affiché
-ni,--no-ignore-case : distinction minuscule ou Majuscule
-w,--word : recherche du mot cat -> 'xx/cat,yy' sera trouvé, mais pas 'xxcatyy'
-v,--invert-match : recherche inversée
-AN : affiche N lignes suivantes (After ; seulement pour entrée | )
-q,--quiet : sortie muette, test uniquement (seulement pour entrée | )
-nc,--no-color : suppression sortie couleur
-h,--help : affiche cette aide
Le plus simple pour estimer si fgp présente un intérêt relativement à grep est de comparer à ce que trouve grep.
Ouvrir un terminal pour fgp, et un autre pour grep, et comparer commande par commande:
fgp '^[decus]' /etc/apt/s{,*/}*[st]
fgp -a 'firefox'
fgp '(modules=|blacklist)' /etc/ /usr/share/
fgp '^P.*(esr|rbird)$' /var/
fgp -v '^(#|$)' /etc/dpkg/*.cfg
lspci -v | fgp -A7 audio
man cp | fgp '-deref'
fgp:
#!/usr/bin/bash
# 20241109 ~ @verner
# fgp : outil de recherche contenu fichiers, à récursivité adaptative
# ――――――――
_help() { cat <<EOF | \
sed '/^ -/s/[^:]*/\x1b[38;5;123m &\x1b[m/;s/fgp/\x1b[38;5;120m&\x1b[m/g'
Usage: $ fgp <options> <regex E> <fichier ou rep> <fichier ou rep> ...
Description:
Recherche contenu fichiers à récursivité adaptative et sortie formatée.
La liste d'options accessibles est limitée aux courantes (90% des besoins)
Options par défaut:
* recherche à partir du répertoire courant, si aucun répertoire indiqué;
* récursivité automatique (max 10) jusqu'aux premiers résultats,
ou niveau max imposé par option '-N'
* regex Etendu ; ex: '(abc|xyz)'
* Masjucules/minuscules indifférenciées (inversable avec -ni)
* ne traverse pas les systèmes de fichiers différents
* exclusion des répertoires/fichiers cachés
* exclusion des répertoires .cache, Trash, lost+found, et fichiers binaires
* exclusion des fichiers non autorisés
* avertissement au delà de 100 fichiers trouvés
* détection des variables commençant par un tiret
Options:
-a,--all : recherche aussi dans les répertoires/fichiers cachés
-N : profondeur max N de recherche répertoires
-r : récursivité (max 10)
-mN : affichage limité aux N premières lignes trouvées par fichier (ex: -m2)
-l,--files-with-matches : seul le nom du fichier sera affiché
-w,--word : recherche de 'cat' -> 'xx/cat,yy' sera trouvé, mais pas 'xxcatyy'
-v,--invert-match : recherche inversée
-AN : affiche N lignes suivantes (After ; seulement pour entrée | )
-q,--quiet : sortie muette, test uniquement (seulement pour entrée | )
-ni,--no-ignore-case : distinction minuscule ou Majuscule
-nc,--no-color : suppression sortie couleur
-h,--help : affiche cette aide
Exemples:
fgp '^[scude]' /etc/apt/s{,*/}*[st]
fgp -a 'firefox'
fgp '(modules=|blacklist)' /etc /usr/share
fgp '^P.*(esr|rbird)$' /var/
fgp -v '^(#|$)' /etc/dpkg/*.cfg
lspci -v | fgp 'audio' -A7
man cp | fgp '-deref'
EOF
}
# ―――――――――
opt=EIhis
for x ; do
case $x in
-a|--all) a=1 ;; # + hiddens
-l|--files-with-matches) l=-l ;; # print file-name only
-w|--word) opt=w$opt ;; # exact word (more restrictive)
-m*) ((${x/-m/}>0)) && mc=$x ;; # max count output
-ni|--no-ignore-case) opt=${opt/i/} ;; # strict case
-A*) ((${x/-A/}>0)) && A=$x ;; # After (pipe input only)
-v|--invert-match) v=-v ;;
-q|--quiet) q=-q ;;
-nc|--no-color) cl=never ;;
-r) md=10 ;;
-h|--help) _help ; exit ;;
-[0-9]*) ((${x/-/}>0)) 2>/dev/null && md=${x/-/} ;; # depth
*) [ -d "$x" ] && R+=("$x") ||
{ [ -f "$x" ] && F+=("$x") || pat="$x" ;} ;;
esac
done
# ―――――――――
[ "$pat" ] || { echo " > argument absent" ; exit 1 ;}
[ "$F$R" ] || R=.
t0=$(date "+%s")
# ―――――――――
[ "$cl" ] || { cl=always; GREP_COLORS='ms=01;36'; p='\x1b[38;5'; c0='\x1b[0m'
cb="$p;75m";cc="$p;123m";cg="$p;120m";co="$p;215m";cy="$p;228m"; }
# ――――――――
if [ -p /dev/stdin ] ; then
cat /dev/stdin | grep -$opt $v $q $mc $A --color=$cl -- "$pat" ; exit
fi
# ――――――――
p1="${pat/^/^[0-9]*:}" ; grep -q '\[' <<<"$pat" && p2=¤ || p2="${pat/^/}"
_gp() { grep -n$opt $mc -- "$pat" "$f" |sed 's#\(.\{120\}\)#\1\n#g' | \
sed -rn "\%$p1%I{s/^[0-9]*:/${cy}& $c0/;s,$p2,${cc}&$c0,gI;p}"
echo ; }
# ―――――――――
if [ "$F" ] ; then
if [ "$v" ] ; then grep -ETvn $l --color=$cl -- "$pat" "${F[@]}"
else while read f ; do sed "s,[^/]*$,${cg}&$c0," <<<"$f"
[ "$l" ] || _gp
done< <(grep -l -$opt -- "$pat" "${F[@]}")
fi
fi
if [ "$R" ] ; then fmax=100; D=1
[ "$v" ] && echo " > -v non compatible argument répertoire" && exit 1
msg1() { printf "――――\n» $lim$co$z$c0 fichiers sous "
printf "$cb$(echo "${R[@]}")$c0 | depth: $co$D$c0 "
T=$(($(date "+%s")-$t0))
if (($T>1)) ; then S=$((T%60))s ; M=$(((T%3600)/60))mn
(($T<60)) && printf "| ~ $S" || printf "| ~ $M $S"
fi
echo ; }
{ [ "$a" ] || [[ ${R/\/.//} != $R ]] ;} && hd='/¤' || hd='.*/\..*'
for Rx in "${R[@]}" ; do d=1 ; y=0 ; [ "$md" ] && M=$md || M=1
while ((d<=M)) ; do ex=1
while read f ; do
if ((d>1)) && [ "$ex" ] ; then ex= ; ((M>D)) && D=$M
printf "${co}%0.s../" $(seq 1 $((d-1))) ; printf " $d ${c0}\n"
fi
sed "s,$Rx,${cb}&$c0,;s,[^/]*$,${cg}&$c0," <<<"$f"
((y++)) ; ((z++))
[ "$l" ] || _gp
# ~~~~~~~~~~~
if ((z>=fmax)) ; then lim='Plus de ' ; msg1
read -t 15 -p "On continue ? [o,y / n] " q </dev/tty
[[ $q == [oOyY]* ]] && fmax=$((fmax+100)) || { echo ; exit; }
fi
# ~~~~~~~~~~~
done< <(find "$Rx" -mount -follow -mindepth $d -maxdepth $d \
\( -regex "${hd}\|\.cache\|Trash\|lost\+found\|recent" \
-o ! -readable -prune \) -o -type f -print0 2>/dev/null | \
xargs -0 grep -l -$opt -- "$pat" |LC_ALL=C sort -V)
((z>20)) && l=-l
[ -z "$md" ] && ((y<10)) && ((M<10)) && ((M++))
((d++))
done
done
if ((z==0)); then printf "${cy}» Rien trouvé / Options possibles:\n"
[ "$a" ] || printf " * recherche dans les fichiers cachés: -a\n"
printf " * modifier le répertoire de recherche ${c0}\n"
exit 1
elif ((z>3)); then msg1
fi
fi
# ―――――――――
exit
Exemples de sortie (partielle):
► Afin de garder ce sujet exploitable et de prévenir d’éventuels débordements hors-sujet,
tout commentaire/question doit être abordé dans le sujet dédié: → Observations sur fgp