diff --git a/uawk b/uawk index 973718e..60e4f25 100755 --- a/uawk +++ b/uawk @@ -1,7 +1,7 @@ #!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 -ALIASES=(awkrun awkfsv2csv awkcsv mergecsv sortcsv) +ALIASES=(awkrun awkfsv2csv awkcsv grepcsv mergecsv sortcsv) if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then # créer les liens scriptname="$(basename "$0")" @@ -40,6 +40,17 @@ $__AWKCSV_HELP" "${prefix}awkcsv" "$@" +elif [ "${scriptname#l}" == "grepcsv" ]; then + [ $# -eq 1 -a "$1" == --help ] && exit_with uecho "$scriptname: wrapper autour de awk pour rechercher des lignes d'un fichier csv +${Clang:+note: ce script ne tient pas compte de la locale: il travaille avec LANG=C +} +USAGE + $scriptname EXPR [inputfiles...] [-- headers...] + +$__GREPCSV_HELP" + + "${prefix}grepcsv" "$@" + elif [ "${scriptname#l}" == "awkfsv2csv" ]; then [ $# -eq 1 -a "$1" == --help ] && exit_with uecho "$scriptname: wrapper autour de awk pour transformer un fichier à colonnes fixes en fichier csv ${Clang:+note: ce script ne tient pas compte de la locale: il travaille avec LANG=C diff --git a/ulib/.ulib_version b/ulib/.ulib_version index 6f4247a..f64f5d8 100644 --- a/ulib/.ulib_version +++ b/ulib/.ulib_version @@ -1 +1 @@ -26 +27 diff --git a/ulib/awk b/ulib/awk index fb31bbd..b3ce51c 100644 --- a/ulib/awk +++ b/ulib/awk @@ -248,7 +248,7 @@ Analyse du flux en entrée: Lire la liste des champs à partir de la première ligne non ignorée du flux. Si la liste des champs est vide, cette option est implicitement activée. Par contre, si une liste de champs est spécifiée et que le flux en entrée - contient un ligne d'en-têtes, il *faut* spécifier explicitement cette + contient une ligne d'en-têtes, il *faut* spécifier explicitement cette option. --sepconf 'CQE' Spécifier en une seule option les caractères de séparation des colonnes (C), @@ -769,6 +769,159 @@ function awkcsv() { LANG=C lawkcsv "$@"; } ################################################################################ +__GREPCSV_HELP="\ +Faire une recherche dans un flux csv, et afficher uniquement les lignes qui +correspondent à l'expression. +EXPR est une expression awk, e.g. 'field == \"value1\" || field == \"value2\"' + +Analyse du flux en entrée: +-s, --skip-lines nblines + Sauter nblines au début du flux +-h, --parse-headers + Lire la liste des champs à partir de la première ligne non ignorée du flux. + Si la liste des champs est vide, cette option est implicitement activée. + Par contre, si une liste de champs est spécifiée et que le flux en entrée + contient un ligne d'en-têtes, il *faut* spécifier explicitement cette + option. +--sepconf 'CQE' + Spécifier en une seule option les caractères de séparation des colonnes (C), + le caractère pour encadrer les chaines (Q) et le caractère d'échappement + (E). Pour chacun des caractères, s'il n'est pas spécifié, la valeur par + défaut est prise. CQE vaut par défaut ',\"' +--colsep C +--qchar Q +--echar E + Spécifier les caractères pour l'analyse du flux csv. Seul le premier + caractère de respectivement C, Q et E est considéré. + - C est le séparateur des colonnes, e.g \",\" + - Q est le caractère employé pour encadrer la valeur d'une colonne si elle + contient le caractère C + - E est le caractère employé pour mettre en echappement le caractère Q dans + la valeur d'une colonne. Si E est vide, le caractère Q doit être doublé. + +Flux en sortie: +-n, --no-headers + Ne pas afficher les en-têtes. Par défaut, les en-têtes sont affichés. +-q, --quiet + Ne pas afficher les lignes. Indiquer seulement si des lignes ont été + trouvées. + +Scripts: +-v var=value + Définir une variable pour le script awk +-e script + Instructions à exécuter pour chaque ligne en entrée. Utiliser la variante + --re pour remplacer les instructions par défaut qui sont exécutées avant les + instructions de -e + Par défaut, il n'y a pas d'instructions -e et les instructions de --re sont + de créer des variables nommées d'après les colonnes du flux csv. Il est + recommandé de ne pas modifier --re + Note d'implémentation: pour des raisons d'optimisation, le traitement + effectué sur chaque ligne n'est pas aussi complet que celui de la fonction + awkcsv(): Les tableaux ORIGFIELDS et ORIGHEADERS sont disponible; par + contre, la ligne courante est au format csv non analysé. Utiliser la + fonction copyall() pour être dans les mêmes conditions que le script awkcsv." + +function lgrepcsv() { + local -a args vars + local skip_lines= parse_headers= + local sepconf= colsep= qchar= echar= + local no_headers= quiet= + local awkscript=--use-default-- + local append_awkscript= + if parse_opts \ + -s:,--skip-lines: skip_lines= \ + -h,--parse-headers parse_headers=1 \ + --sepconf: sepconf= \ + --colsep: colsep= \ + --qchar: qchar= \ + --echar: echar= \ + -n,--no-headers no_headers=1 \ + --show-headers no_headers= \ + -q,--quiet quiet=1 \ + -v:,--var: vars \ + --re: '$set@ awkscript; append_awkscript=' \ + -e:,--e:,--awk-script:,--script: append_awkscript= \ + @ args -- "$@"; then + set -- "${args[@]}" + else + eerror "$args" + return 1 + fi + + local expr="$1"; shift + + local -a inputfiles tmpfiles + local inputfile + while [ $# -gt 0 -a "$1" != "--" ]; do + if [ "$1" == "-" ]; then + ac_set_tmpfile inputfile + array_add tmpfiles "$inputfile" + cat >"$inputfile" + else + inputfile="$1" + fi + array_add inputfiles "$inputfile" + shift + done + shift + if [ "${#inputfiles[*]}" -eq 0 ]; then + ac_set_tmpfile inputfile + array_add tmpfiles "$inputfile" + cat >"$inputfile" + array_add inputfiles "$inputfile" + fi + + local -a headers + headers=("$@") + if [ -z "${headers[*]}" ]; then + parse_headers=1 + array_from_lines headers "$(awkrun -f lskip:int="$skip_lines" 'NR <= lskip { next } +{ + count = array_parsecsv(__fields, $0) + for (i = 1; i <= count; i++) { + print __fields[i] + } + exit +}' -- "${inputfiles[@]}")" + fi + if [ "$awkscript" == --use-default-- ]; then + awkscript= + for header in "${headers[@]}"; do + awkscript="$awkscript +$header = oget(\"$header\")" + done + fi + local grepscript="\ +BEGIN { ec = 1 } +{ + $awkscript + $append_awkscript + if ($expr) { + ec = 0 + if (quiet) exit + printheaders() + print + } +} +END { exit ec }" + + lawkcsv ${skip_lines:+-s "$skip_lines"} ${parse_headers:+-h} \ + ${sepconf:+--sepconf "$sepconf"} ${colsep:+--colsep "$colsep"} ${qchar:+--qchar "$qchar"} ${echar:+--echar "$echar"} \ + ${no_headers:+--no-headers} -v quiet:int=$quiet \ + --re '{array_parsecsv(ORIGFIELDS, $0, length(ORIGHEADERS))}' -a "$grepscript" \ + -- "${headers[@]}" -- "${inputfiles[@]}" + local r=$? + + ac_clean "${tmpfiles[@]}" + return $r +} + +function cgrepcsv() { LANG=C lgrepcsv "$@"; } +function grepcsv() { LANG=C lgrepcsv "$@"; } + +################################################################################ + __AWKFSV2CSV_HELP="\ Transformer un flux fsv (colonnes à largeurs fixes) en csv