nutools/umatch

134 lines
4.0 KiB
Bash
Executable File

#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
function display_help() {
uecho "$scriptname: Afficher le résultat d'une recherche par regexp et compter
éventuellement leurs occurences
USAGE
$scriptname [options] [regexp]
Chaque ligne *entière* de stdin est mise en correspondance avec l'expression
regulière qui doit avoir la syntaxe de awk. S'il y a correspondance, afficher
soit toute l'expression matchée, soit chaque groupe s'il y a des expressions
parenthésées, chacun des groupes étant séparé par le caractère sep.
Si l'expression régulière n'est pas spécifiée, elle vaut par défaut '.*' ce qui
fait que toutes les lignes correspondent. Ceci peut être utile avec les options
-s et -c.
Certaines expressions régulières sont prédéfinies. regexp peut avoir l'une des
valeurs prédéfinies suivantes:
ip --> "'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'"
OPTIONS
-F sep
Spécifier le caractère séparateur en sortie s'il faut afficher des
champs multiples. Par défaut, utiliser le caractère ':'
Si un séparateur vide est spécifié, le séparateur standard de awk est
utilisé.
-s Trier le résultat
-C FIELDNUM
-c, --count
Compter les occurences successives de la valeur du champ FIELDNUM, et
afficher une ligne de la forme 'count<sep>last_line' pour chaque groupe,
last_line étant la dernière occurence du groupe. L'option -c correspond
à '-C 0', c'est à dire compter les occurences successives de toute les
lignes.
Le séparateur utilisé pour calculer le numéro de champ est sep, spécifié
avec l'option -F.
-a, --all-lines
Avec les options -c/-C, afficher toutes les lignes des occurences
successives au lieu de seulement la ligne de la dernière occurence. Ceci
est utile surtout avec l'option -C, pour voir les différences avec les
différentes lignes.
-m, --multiple
Avec les options -c/-C, n'afficher que les lignes dont le compte est
supérieur à 1. Avec l'option -a, les groupes contigus sont séparés par
une ligne '--'"
}
function match() {
local regexp="$1" sep="$2"
awkrun ${sep:+FS="$sep"} '{
pos = match($0, /'"${regexp//\//\\/}"'/, out)
if (pos != 0) {
max = length(out) / 3
if (max == 1) {
print out[0]
} else {
line = ""
for (i = 1; i < max; i++) {
if (i > 1) line = line FS
line = line out[i]
}
print line
}
}
}'
}
function count() {
local field="$1" sep="$2" all_lines="$3"
awkrun field="$field" ${sep:+FS="$sep"} all_lines="$all_lines" '
BEGIN {
first = 1
lastfield = ""
lastline = ""
count = 0
if (all_lines == "") all_lines = 0
else all_lines = 1
}
!first && $field != lastfield {
if (!all_lines) print count FS lastline
count = 0
}
{
count = count + 1
lastfield = $field
lastline = $0
first = 0
if (all_lines) print count FS lastline
}
END {
print count FS lastline
}
'
}
sep=:
sort=
count=
all_lines=
multiple=
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-F: sep= \
-s sort=1 \
-c,--count count=0 \
-C: count= \
-a,--all-lines all_lines=1 \
-m,--multiple multiple=1 \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
regexp="$1"
[ -n "$regexp" ] || regexp=".*"
case "$regexp" in
ip) regexp='[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+';;
esac
cmd="$(qvals match "$regexp" "$sep")"
[ -n "$sort" ] && cmd="$cmd | sort"
[ -n "$count" ] && cmd="$cmd | $(qvals count "$count" "$sep" "$all_lines")"
if [ -n "$multiple" ]; then
if [ -n "$all_lines" ]; then
cmd="$cmd | $(qvals grep -v -B 1 "^1$sep")"
else
cmd="$cmd | $(qvals grep -v "^1$sep")"
fi
fi
eval "$cmd"