2013-08-27 15:14:44 +04:00
|
|
|
##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
## fonctions pour awk
|
|
|
|
##@cooked nocomments
|
|
|
|
##@require base
|
|
|
|
uprovide awk
|
|
|
|
urequire base
|
|
|
|
|
|
|
|
__AWKDEF_HELP="\
|
|
|
|
Les variables données en arguments sont définies dans une section BEGIN{}. Si
|
|
|
|
une valeur ne ressemble pas à une définition de variable, l'analyse des
|
|
|
|
variables s'arrête et le reste des arguments est inséré tel quel.
|
|
|
|
|
|
|
|
Normalement, les variables définies sont scalaires, avec une syntaxe de la forme
|
2016-07-05 13:11:27 +04:00
|
|
|
name:str=value pour une chaine, name:int=value pour une valeur entière, ou
|
|
|
|
name=value pour déterminer automatiquement le type: entier si la valeur ne
|
|
|
|
contient que des chiffres, chaine sinon.
|
2013-08-27 15:14:44 +04:00
|
|
|
|
|
|
|
Il est aussi possible d'utiliser la syntaxe awk_array[@]=bash_array pour
|
|
|
|
initialiser le tableau awk_array, qui contiendra toute les valeurs du tableau
|
|
|
|
nommé bash_array, avec les indices de 1 à N, N étant le nombre d'éléments du
|
|
|
|
tableau bash_array. La variable awk_array_count est aussi initialisée, et
|
|
|
|
contient le nombre d'éléments du tableau.
|
|
|
|
La syntaxe simplifiée array[@] est équivalente à array[@]=array
|
|
|
|
Il existe une autre syntaxe 'awk_array[@]=<' qui permet de spécifier les valeurs
|
|
|
|
du tableau, une par ligne, e.g:
|
|
|
|
$'values[@]=<\nvalue1\nvalue2'
|
|
|
|
pour un tableau values qui contiendra deux valeurs: value1 et value2
|
|
|
|
|
|
|
|
Les fonctions suivantes sont définies:
|
|
|
|
|
2015-10-09 14:16:40 +04:00
|
|
|
num(s)
|
|
|
|
si s ne contient que des chiffres, retourner la valeur numérique associée,
|
|
|
|
sinon retournée la valeur inchangée
|
|
|
|
|
|
|
|
ord(s)
|
|
|
|
retourner le code ASCII du premier caractère de la chaine s. seuls les codes
|
|
|
|
de 32 à 127 sont supportés
|
|
|
|
|
|
|
|
hex(i)
|
|
|
|
retourner la représentation hexadécimale du nombre i
|
|
|
|
|
|
|
|
qhtml(s)
|
|
|
|
remplacer respectivement dans la chaine s les valeurs &, \", > et < par
|
|
|
|
&, ", > et <
|
|
|
|
L'alias quote_html(s) existe pour compatibilité
|
|
|
|
|
|
|
|
unquote_html(s)
|
|
|
|
faire le contraire de qhtml(s)
|
|
|
|
|
|
|
|
qval(s)
|
2013-08-27 15:14:44 +04:00
|
|
|
quoter une valeur pour le shell. la valeur est entourée de quotes, e.g:
|
2015-10-09 14:16:40 +04:00
|
|
|
qval(\"here, \\\"there\\\" and 'everywhere'.\")
|
2013-08-27 15:14:44 +04:00
|
|
|
--> 'here, \"there\" and '\\''everywhere'\\''.'
|
2015-10-09 14:16:40 +04:00
|
|
|
L'alias quote_value(s) existe pour compatibilité
|
|
|
|
|
2016-02-15 15:09:17 +04:00
|
|
|
sqval(s)
|
2015-10-09 14:16:40 +04:00
|
|
|
comme qval() mais ajouter un espace avant la valeur quotée. ceci permet de
|
|
|
|
construire facilement une ligne de commande, e.g.:
|
2016-02-15 15:09:17 +04:00
|
|
|
print \"mycmd\" sqval(arg1) sqval(arg2)
|
|
|
|
L'alias qsval(s) existe pour compatibilité
|
2013-08-27 15:14:44 +04:00
|
|
|
|
2015-10-09 14:16:40 +04:00
|
|
|
qvals()
|
2013-08-27 15:14:44 +04:00
|
|
|
quoter les valeurs \$1..\$NF pour les passer comme argument sur la ligne de
|
|
|
|
commande avec eval. e.g.:
|
2015-10-09 14:16:40 +04:00
|
|
|
print \"mycmd \" qvals()
|
2013-08-27 15:14:44 +04:00
|
|
|
La ligne qui est affichée pourra être évaluée avec eval dans le shell.
|
2015-10-09 14:16:40 +04:00
|
|
|
L'alias quoted_values(s) existe pour compatibilité
|
|
|
|
|
2016-02-15 15:09:17 +04:00
|
|
|
sqvals(s)
|
2015-10-09 14:16:40 +04:00
|
|
|
comme qvals() mais ajouter un espace avant la valeur quotée. ceci permet de
|
|
|
|
construire facilement une ligne de commande, e.g.:
|
2016-02-15 15:09:17 +04:00
|
|
|
print \"mycmd\" sqvals()
|
|
|
|
L'alias qsvals(s) existe pour compatibilité
|
2013-08-27 15:14:44 +04:00
|
|
|
|
2016-02-29 23:55:54 +04:00
|
|
|
qarr(vs[, prefix])
|
2016-02-26 11:19:30 +04:00
|
|
|
quoter les valeurs du tableau vs pour le shell, e.g:
|
|
|
|
print \"values=(\" qarr(values) \")\"
|
2016-02-29 22:01:49 +04:00
|
|
|
si prefix est spécifié, il est affiché suivi d'un espace, suivi des valeurs
|
|
|
|
du tableau, ce qui permet de construire une ligne de commande, e.g.:
|
|
|
|
print qarr(values, \"mycmd\")
|
2016-02-26 11:19:30 +04:00
|
|
|
|
2015-10-09 14:16:40 +04:00
|
|
|
qsubrepl(s)
|
2013-08-27 15:14:44 +04:00
|
|
|
quoter une valeur pour l'argument r des fonctions sub() et gsub(). Les
|
|
|
|
caractères suivants sont mis en échappement: \\ &
|
2015-10-09 14:16:40 +04:00
|
|
|
L'alias quote_subrepl(s) existe pour compatibilité
|
2013-08-27 15:14:44 +04:00
|
|
|
|
2015-10-09 14:16:40 +04:00
|
|
|
qgrep(s)
|
2013-08-27 15:14:44 +04:00
|
|
|
quoter une valeur pour un pattern *simple* de grep. Les caractères suivants
|
|
|
|
sont mis en échappement: \\ . [ ^ \$ *
|
2015-10-09 14:16:40 +04:00
|
|
|
L'alias quote_grep(s) existe pour compatibilité
|
2013-08-27 15:14:44 +04:00
|
|
|
|
2015-10-09 14:16:40 +04:00
|
|
|
qegrep(s)
|
2013-08-27 15:14:44 +04:00
|
|
|
quoter une valeur pour un pattern *étendu* de grep. Les caractères suivants
|
|
|
|
sont mis en échappement: \\ . [ ^ \$ ? + * ( ) | {
|
2015-10-09 14:16:40 +04:00
|
|
|
L'alias quote_egrep(s) existe pour compatibilité
|
2013-08-27 15:14:44 +04:00
|
|
|
|
2016-04-15 12:23:27 +04:00
|
|
|
qsql(s, [suffix])
|
2013-08-27 15:14:44 +04:00
|
|
|
quoter une valeur pour un script sql. la valeur est entourée de quotes, e.g:
|
2016-02-15 15:09:17 +04:00
|
|
|
qsql(\"hello'there\")
|
2013-08-27 15:14:44 +04:00
|
|
|
--> 'hello''there'
|
2015-10-09 14:16:40 +04:00
|
|
|
L'alias quote_sql(s) existe pour compatibilité
|
2016-04-15 12:23:27 +04:00
|
|
|
Si suffix est spécifié, le rajouter après la valeur précédé d'un espace, e.g:
|
|
|
|
qsql(\"value\", \"field\")
|
|
|
|
--> 'value' field
|
2013-08-27 15:14:44 +04:00
|
|
|
|
2016-04-15 12:23:27 +04:00
|
|
|
cqsql(s, [suffix])
|
2016-02-15 15:09:17 +04:00
|
|
|
comme qsql() mais ajouter une virgule avant la valeur quotée. ceci permet de
|
|
|
|
construire facilement une requête SQL, e.g:
|
|
|
|
print \"insert into t(a, b, c) values (\" qsql(a) cqsql(b) cqsql(c) \");\"
|
2016-04-15 12:23:27 +04:00
|
|
|
--> insert into t(a, b, c) values ('a','b','c');
|
|
|
|
Si suffix est spécifié, le rajouter après la valeur précédé d'un espace, e.g:
|
|
|
|
cqsql(\"value\", \"field\")
|
|
|
|
--> ,'value' field
|
2016-02-15 15:09:17 +04:00
|
|
|
|
2014-02-27 11:51:46 +04:00
|
|
|
unquote_mysqlcsv(s)
|
|
|
|
Analyser une valeur exportée de MySQL avec mysqlcsv. Les transformations
|
|
|
|
suivantes sont effectuées:
|
|
|
|
\\n --> <newline>
|
|
|
|
\\t --> <tab>
|
2014-02-27 14:35:00 +04:00
|
|
|
\\0 --> <caractère NUL>
|
2014-02-27 11:51:46 +04:00
|
|
|
\\\\ --> \\
|
|
|
|
|
2016-02-15 15:09:17 +04:00
|
|
|
sval(s)
|
|
|
|
retourner la valeur s précédée d'un espace si elle est non vide, e.g:
|
|
|
|
sval(\"\") --> \"\"
|
|
|
|
sval(\"any\") --> \" any\"
|
|
|
|
|
2016-04-15 12:23:27 +04:00
|
|
|
cval(s, [suffix])
|
2016-02-15 15:09:17 +04:00
|
|
|
retourner la valeur s précédée d'une virgule si elle est non vide, e.g:
|
|
|
|
sval(\"\") --> \"\"
|
|
|
|
sval(\"any\") --> \",any\"
|
2016-04-15 12:23:27 +04:00
|
|
|
Si suffix est spécifié, le rajouter après la valeur précédé d'un espace, e.g:
|
|
|
|
cval(\"\", \"field\") --> \"\"
|
|
|
|
cval(\"value\", \"field\") --> \",value field\"
|
2016-02-15 15:09:17 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
mkindices(values, indices)
|
|
|
|
créer le tableau indices qui contient les indices du tableau values, de 1 à N,
|
|
|
|
et retourner la valeur N. Il faudra utiliser les valeurs de cette manière:
|
|
|
|
count = mkindices(values, indices)
|
|
|
|
for (i = 1; i <= count; i++) {
|
|
|
|
value = values[indices[i]]
|
|
|
|
...
|
|
|
|
}
|
|
|
|
cette fonction nécessite gnuawk
|
|
|
|
|
|
|
|
array_new(dest)
|
|
|
|
créer un nouveau tableau vide dest
|
|
|
|
|
|
|
|
array_newsize(dest, size)
|
|
|
|
créer un nouveau tableau de taille size, rempli de chaines vides
|
|
|
|
|
2016-02-15 15:09:17 +04:00
|
|
|
array_len(src)
|
|
|
|
calculer la taille d'un tableau. length(array) a un bug sur GNUawk 3.1.5.
|
|
|
|
cette fonction est plus lente, mais fonctionne sur toutes les versions de
|
|
|
|
awk et GNUawk
|
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
array_copy(dest, src)
|
|
|
|
faire une copie d'un tableau. Cette fonction nécessite gnuawk, puisqu'elle
|
|
|
|
utilise mkindices().
|
|
|
|
|
|
|
|
array_getlastindex(src)
|
|
|
|
Retourner l'index du dernier élément du tableau src
|
|
|
|
|
|
|
|
array_add(dest, value)
|
|
|
|
Ajouter un élément dans dest, avec l'index array_getlastindex(dest)+1
|
|
|
|
|
|
|
|
array_deli(dest, i)
|
|
|
|
Supprimer l'élément d'index i dans le tableau dest.
|
|
|
|
Les index des éléments après i sont corrigés en conséquence.
|
|
|
|
Cette fonction assume que les indices du tableau commencent à 1 et n'ont pas
|
|
|
|
de \"trous\". Si i==0, cette fonction est un NOP.
|
|
|
|
|
2014-08-18 11:25:57 +04:00
|
|
|
array_del(dest, value[, ignoreCase])
|
2013-08-27 15:14:44 +04:00
|
|
|
Supprimer *tous* les éléments du tableau dest dont la valeur est value. Les
|
|
|
|
indexes des valeurs sont trouvées avec key_index(), puis les valeurs sont
|
|
|
|
supprimées avec array_deli()
|
2014-08-18 11:25:57 +04:00
|
|
|
ignoreCase permet de spécifier que la recherche de la valeur se fait en
|
|
|
|
ignorant la casse.
|
2013-08-27 15:14:44 +04:00
|
|
|
|
|
|
|
array_extend(dest, src)
|
|
|
|
Ajouter les éléments de src dans dest, en commençant avec l'index
|
|
|
|
array_getlastindex(dest)+1
|
|
|
|
|
|
|
|
array_fill(dest)
|
|
|
|
remplir le tableau avec \$1..\$NF
|
|
|
|
|
|
|
|
array_getline(src)
|
|
|
|
avec le tableau array contenant N éléments, initialise \$1..\$N avec les valeurs
|
|
|
|
du tableau
|
|
|
|
|
|
|
|
array_appendline(src)
|
|
|
|
avec le tableau array contenant N éléments, initialise \$(NF+1)..\$(NF+N) avec
|
|
|
|
les valeurs du tableau
|
|
|
|
|
|
|
|
in_array(value, values[, ignoreCase])
|
|
|
|
tester si le tableau values contient la valeur value, en ne tenant
|
|
|
|
éventuellement pas compte de la casse.
|
|
|
|
|
|
|
|
key_index(value, values[, ignoreCase])
|
|
|
|
trouver l'index de value dans le tableau values, en ne tenant éventuellement
|
|
|
|
pas compte de la casse. Retourner 0 si la valeur n'a pas été trouvée
|
|
|
|
|
|
|
|
array2s(values, prefix, sep, suffix, noindices)
|
|
|
|
convertir un tableau en chaine pour affichage. attention! les valeurs sont
|
|
|
|
affichés dans un ordre arbitraire. noindices, s'il vaut 1, supprime
|
|
|
|
l'affichage des indices du tableau. prefix (qui vaut par défaut \"[\") est
|
|
|
|
ajouté avant la chaine, suffix (qui vaut par défaut \"]\") après, et sep (qui
|
|
|
|
vaut par défaut \",\") est utilisé pour séparer chaque valeur.
|
|
|
|
|
|
|
|
array2so(values, prefix, sep, suffix, noindices)
|
|
|
|
convertir un tableau en chaine pour affichage. Les valeurs sont affichés dans
|
|
|
|
l'ordre du tableau. Cette fonction nécessite gnuawk, puisqu'elle utilise
|
|
|
|
mkindices(). noindices, s'il vaut 1, supprime l'affichage des indices du
|
|
|
|
tableau. prefix est ajouté avant la chaine, suffix après, et sep (qui vaut par
|
|
|
|
défaut \",\") est utilisé pour séparer chaque valeur.
|
|
|
|
|
|
|
|
array_join(values, sep, prefix, suffix)
|
|
|
|
convertir un tableau en chaine pour affichage. Les valeurs sont affichés dans
|
|
|
|
l'ordre du tableau. Cette fonction nécessite gnuawk, puisqu'elle utilise
|
|
|
|
mkindices().
|
|
|
|
Il n'y a pas de valeur par défaut pour sep, prefix et suffix.
|
|
|
|
|
|
|
|
printto(s, output)
|
|
|
|
en fonction de la valeur de output, afficher la chaine s sur la destination
|
|
|
|
spécifiée.
|
|
|
|
output est de la forme... faire....
|
|
|
|
\"\" print s
|
|
|
|
\"dest\" print s >\"dest\"
|
|
|
|
\">dest\" print s >\"dest\"
|
|
|
|
\">>dest\" print s >>\"dest\"
|
|
|
|
XXX les formes suivantes sont désactivées pour le moment:
|
|
|
|
\"|dest\" print s |\"dest\"
|
|
|
|
\"|&dest\" print s |&\"dest\"
|
|
|
|
|
|
|
|
find_line(input, field, value)
|
|
|
|
retourner la première ligne du fichier input dont le champ \$field vaut
|
|
|
|
value. Retourner une chaine vide si la ligne n'a pas été trouvée.
|
|
|
|
|
|
|
|
merge_line(input, field, key)
|
|
|
|
équivaut à:
|
|
|
|
\$0 = \$0 FS find_line(input, field, \$key)
|
|
|
|
La ligne courante n'est pas modifiée si la ligne correspondante dans input
|
|
|
|
n'est pas trouvée.
|
|
|
|
|
|
|
|
array_parsecsv2(fields, line, nbfields, colsep, qchar, echar)
|
|
|
|
array_parsecsv(fields, line, nbfields[, colsep, qchar, echar])
|
|
|
|
analyser une ligne au format csv, et initialiser le tableau fields aux valeurs
|
|
|
|
des champs trouvés. Si nbfields est spécifié, c'est le nombre de champs
|
|
|
|
minimum que doit avoir le tableau résultat. Le tableau est complété avec des
|
|
|
|
chaines vides au besoin. Le tableau commence à l'index 1.
|
|
|
|
Consulter array_formatcsv2 pour la signification de colsep, qchar et echar.
|
|
|
|
array_parsecsv(fields, line, nbfields) est équivalent à
|
|
|
|
array_parsecsv2(fields, line, nbfields, ",", "\"", "")
|
|
|
|
|
|
|
|
parsecsv(line)
|
|
|
|
équivaut à:
|
|
|
|
array_parsecsv(fields, line)
|
|
|
|
array_getline(fields)
|
|
|
|
|
|
|
|
getlinecsv(file)
|
|
|
|
obtient une ligne par getline, l'analyse, puis initialise \$1..\$N avec les
|
|
|
|
valeurs trouvées. equivaut à:
|
|
|
|
getline
|
|
|
|
parsecsv(\$0)
|
|
|
|
Si file est spécifié, utiliser 'getline <file' au lieu de 'getline'
|
|
|
|
|
|
|
|
array_formatcsv2(fields, colsep, mvsep, qchar, echar)
|
|
|
|
construit une ligne au format csv avec les colonnes du tableau fields.
|
|
|
|
- colsep est le séparateur des colonnes, e.g \",\"
|
|
|
|
- mvsep est le séparateur des valeurs des colonnes (si une colonne contient
|
|
|
|
plusieurs valeurs, par exemple si elle transporte la valeur d'un attribut
|
|
|
|
multivalué). Cette information ne sert que pour décider s'il faut encadrer
|
|
|
|
la colonne avec qchar.
|
|
|
|
- qchar est le caractère à employer pour encadrer la valeur d'une colonne
|
|
|
|
si elle contient l'un des caractères colsep ou mvsep
|
|
|
|
- echar est le caractère à employer pour mettre en echappement le caractère
|
|
|
|
qchar dans la valeur d'une colonne. Si echar est vide, le caractère qchar
|
|
|
|
est doublé.
|
|
|
|
|
|
|
|
array_formatcsv(fields, output)
|
|
|
|
équivaut à
|
|
|
|
array_formatcsv2(fields, \",\", \";\", \"\\\"\", \"\")
|
|
|
|
|
|
|
|
array_printcsv(fields, output)
|
|
|
|
équivaut à
|
|
|
|
printto(array_formatcsv(fields), output)
|
|
|
|
|
|
|
|
get_formatcsv()
|
|
|
|
équivaut à
|
|
|
|
array_fill(fields)
|
|
|
|
return array_formatcsv(fields)
|
|
|
|
|
|
|
|
formatcsv()
|
|
|
|
équivaut à
|
2014-01-23 11:19:05 +04:00
|
|
|
\$0 = get_formatcsv()
|
2013-08-27 15:14:44 +04:00
|
|
|
|
|
|
|
printcsv(output)
|
|
|
|
équivaut à
|
|
|
|
array_fill(fields)
|
|
|
|
array_printcsv(fields, output)
|
|
|
|
|
|
|
|
array_findcsv(fields, input, field, value, nbfields)
|
|
|
|
initialiser le tableau fields avec la première ligne au format csv du fichier
|
|
|
|
input dont le champ \$field vaut value. Le tableau est toujours initialisé,
|
|
|
|
même si la ligne correspondante n'a pas été trouvée.
|
|
|
|
Retourner 1 si la ligne correspondante a été trouvée
|
|
|
|
Si nbfields est spécifié, c'est le nombre de champs minimum que doit avoir le
|
|
|
|
tableau résultat. Le tableau est complété avec des chaines vides au besoin. Le
|
|
|
|
tableau commence à l'index 1."
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
|
|
|
|
__AWKCSV_HELP="\
|
|
|
|
Lancer un script awk pour traiter un flux csv
|
|
|
|
Typiquement, l'option -e sera utilisée pour faire un traitement sur les
|
|
|
|
données, e.g:
|
|
|
|
awkcsv -e '{ \$2 = toupper(\$2) }'
|
|
|
|
pour mettre en majuscule le deuxième champ.
|
|
|
|
|
|
|
|
Analyse du flux en entrée:
|
2016-02-29 23:27:21 +04:00
|
|
|
-s, -S, --skip-lines nblines
|
2013-08-27 15:14:44 +04:00
|
|
|
Sauter nblines au début du flux
|
2016-02-29 23:27:21 +04:00
|
|
|
-h, -H, --parse-headers
|
2013-08-27 15:14:44 +04:00
|
|
|
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
|
2014-03-03 11:24:17 +04:00
|
|
|
contient une ligne d'en-têtes, il *faut* spécifier explicitement cette
|
2013-08-27 15:14:44 +04:00
|
|
|
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.
|
|
|
|
-z, --reset-fields
|
|
|
|
Commencer le script -e avec les champs vides. Il faut les copier
|
|
|
|
individuellement avec copyfield().
|
|
|
|
|
|
|
|
Scripts:
|
|
|
|
-v var=value
|
|
|
|
Définir une variable pour le script awk
|
|
|
|
-b before
|
|
|
|
Instructions à exécuter avant le début de l'analyse. Utiliser la variante
|
|
|
|
--rb pour remplacer les instructions par défaut, qui sont exécutées avant
|
|
|
|
les instructions de -b
|
|
|
|
Par défaut, il n'y a pas d'instructions -b et les instructions de --rb sont
|
|
|
|
d'analyser les en-têtes si -h est spécifié. Le tableau ORIGHEADERS contient
|
|
|
|
les en-têtes analysées en entrée. Le tableau HEADERS contient les en-têtes
|
|
|
|
spécifiées par l'utilisateur (copie de ORIGHEADERS si l'utilisateur de
|
|
|
|
spécifie pas d'en-têtes). Il est recommandé de ne pas modifier --rb
|
|
|
|
|
|
|
|
-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
|
|
|
|
d'analyser la ligne courante. Les champs sont disponibles dans \$1..\$NF et
|
|
|
|
dans le tableau ORIGFIELDS. Il est recommandé de ne pas modifier --re
|
|
|
|
-m, --map FIELDMAP
|
|
|
|
Renommer les champs selon la liste FIELDMAP, qui contient des éléments de la
|
|
|
|
forme dest:src et séparés par des virgules. Le champ src doit exister dans
|
|
|
|
HEADERS, et dest ne doit pas exister dans HEADERS, sinon l'élément est
|
|
|
|
ignoré. Un script équivalent au suivant est ajouté aux instructions par
|
|
|
|
défaut --re:
|
|
|
|
'{ if (do_once(\"mapfields\")) mapfields(FIELDMAP) }'
|
|
|
|
Cette option annule l'option -z
|
|
|
|
-c, --checkfields FIELDS
|
|
|
|
S'assurer que toutes les en-têtes spécifiées dans la liste FIELDS sont
|
|
|
|
présentes. Sinon, sortir du script avec le code de retour 1. Un script
|
|
|
|
équivalent au suivant est rajouté aux instructions par défaut --re:
|
|
|
|
'{ if (do_once(\"checkfields\") && !checkfields(FIELDS)) exit 1 }'
|
|
|
|
Ce traitement est effectué le cas échéant après le traitement --map
|
|
|
|
Cette option annule l'option -z
|
|
|
|
--checkvalues FIELDS
|
|
|
|
S'assurer que toutes les valeurs des en-têtes spécifiées dans la liste
|
|
|
|
FIELDS sont non vides. Sinon, sortir du script avec le code de retour 1. Un
|
|
|
|
script équivalent au suivant est rajouté aux instructions par défaut --re:
|
|
|
|
'{ if (!checkvalues(FIELDS)) exit 1 }'
|
|
|
|
Ce traitement est effectué le cas échéant après le traitement --checkfields
|
|
|
|
Cette option annule l'option -z
|
2016-02-29 22:01:49 +04:00
|
|
|
-k, --keep-fields KEEPFIELDS
|
2013-08-27 15:14:44 +04:00
|
|
|
Garder les champs spécifiés. Les autres sont supprimés de la sortie.
|
|
|
|
KEEPFIELDS est une liste de champs séparés par des virgules, ou '*' pour
|
|
|
|
spécifier de garder tous les champs, ou '' pour ne garder aucun champ.
|
|
|
|
Si un champ de KEEPFIELDS n'existe pas, il est créé.
|
|
|
|
Cette option annule l'option -z
|
2016-02-29 22:01:49 +04:00
|
|
|
--skip-fields SKIPFIELDS
|
2013-08-27 15:14:44 +04:00
|
|
|
Exclure les champs spécifiés. SKIPFIELDS est une liste de champs séparés par
|
|
|
|
des virgules.
|
|
|
|
Pour les options --keep et --skip, un script équivalent au suivant est
|
|
|
|
rajouté aux instructions par défaut --re:
|
|
|
|
if (do_once(\"filterfields\")) {
|
|
|
|
build_skipfs(KEEPFIELDS, SKIPFIELDS, SKIPFS, ADDFS)
|
|
|
|
filterheaders(SKIPFS, ADDFS)
|
|
|
|
}
|
|
|
|
filterfields(SKIPFS, ADDFS)
|
|
|
|
Ce traitement est effectué le cas échéant après le traitement --checkvalues
|
|
|
|
-a after
|
|
|
|
Instructions à exécuter après avoir traité la ligne en entrée. Utiliser la
|
|
|
|
variante --ra pour remplacer les instructions par défaut qui sont exécutées
|
|
|
|
avant les instructions de -a
|
|
|
|
Par défaut, il n'y a pas d'instructions --ra et les instructions de -a sont
|
|
|
|
d'afficher les en-têtes si -n n'est pas spécifié. Puis afficher les champs
|
|
|
|
\$1..\$NF
|
|
|
|
|
|
|
|
En fonctions des arguments, les variables skip_lines, parse_headers et
|
|
|
|
show_headers sont définis. user_headers est un tableau contenant les champs
|
|
|
|
spécifiés par l'utilisateur.
|
|
|
|
|
|
|
|
Les fonctions suivantes sont disponibles:
|
|
|
|
do_once(key)
|
|
|
|
Retourner vrai s'il faut faire l'opération identifiées par key (qui vaut
|
|
|
|
par défaut \"default\"), qui ne devrait être faite qu'une seule fois.
|
|
|
|
Utiliser de cette manière:
|
|
|
|
if (do_once(key)) { ... }
|
|
|
|
ogeth(field)
|
|
|
|
ogeti(num)
|
|
|
|
oget(field)
|
|
|
|
Gestion des valeurs originales.
|
|
|
|
geth(field)
|
|
|
|
sethi(num, value)
|
|
|
|
seth(field, newfield)
|
|
|
|
addh(field)
|
|
|
|
delhi(num)
|
|
|
|
delh(field)
|
|
|
|
geti(num)
|
|
|
|
get(field)
|
|
|
|
seti(num, value)
|
|
|
|
set(field, value)
|
|
|
|
add(field, value)
|
|
|
|
deli(num)
|
|
|
|
del(field)
|
|
|
|
Gestion des valeurs courantes.
|
|
|
|
comparevic(field1, value1, field2, value2, icfields)
|
|
|
|
ocompareic(field1, field2, icfields)
|
|
|
|
compareic(field1, field2, icfields)
|
|
|
|
infields(field, fields)
|
|
|
|
parseheaders()
|
|
|
|
printheaders()
|
|
|
|
resetfields()
|
|
|
|
copyfield(field)
|
|
|
|
copyfields(fields)
|
|
|
|
copyall()
|
|
|
|
mapfields(fieldmap)
|
|
|
|
checkfields(fields[, missings])
|
|
|
|
checkvalues(fields[, missings])
|
|
|
|
build_skipfs(keepfields, skipfields, skipfs, addfs)
|
|
|
|
filterheaders(skipfs, addfs)
|
|
|
|
filterfields(skipfs, addfs)
|
|
|
|
Gestion avancée des valeurs. Pour les fonctions comparevic(), ocompareic(),
|
|
|
|
compareic(), infields(), copyfields(), checkfields(), checkvalues(),
|
|
|
|
mapfields(), build_skipfs(), les arguments sont une suite d'éléments séparés
|
|
|
|
par des virgules."
|
|
|
|
|
|
|
|
__AWKCSV_FUNCTIONS='
|
|
|
|
BEGIN {
|
2015-03-08 17:17:15 +04:00
|
|
|
# Forcer ici le type tableau pour les variables ci-dessous. Sinon, leur
|
|
|
|
# utilisation dans une section BEGIN{} provoque une erreur fatale, parce
|
|
|
|
# qu"ils ne sont déclarés que dans des fonctions
|
|
|
|
array_new(HEADERS)
|
|
|
|
array_new(ORIGHEADERS)
|
|
|
|
array_new(ORIGFIELDS)
|
2013-08-27 15:14:44 +04:00
|
|
|
array_new(__DONE_ONCE)
|
|
|
|
}
|
|
|
|
function do_once(key) {
|
|
|
|
if (!key) key = "default"
|
|
|
|
if (__DONE_ONCE[key] != "") return 0
|
|
|
|
__DONE_ONCE[key] = 1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2014-10-04 10:07:05 +04:00
|
|
|
function __geth(field, HEADERS, nbfields, i) {
|
|
|
|
nbfields = array_len(HEADERS)
|
|
|
|
if (int(field) == field) {
|
|
|
|
field = int(field)
|
|
|
|
if (field >= 1 && field <= nbfields) return field
|
|
|
|
else return 0
|
|
|
|
}
|
|
|
|
field = tolower(field)
|
|
|
|
for (i = 1; i <= nbfields; i++) {
|
|
|
|
if (field == tolower(HEADERS[i])) {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function __geti(num, HEADERS) { if (num != 0) return HEADERS[num] }
|
|
|
|
function __get(field, HEADERS) { return __geti(__geth(field, HEADERS), HEADERS) }
|
|
|
|
|
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
function ogeth(field, nbfields, i) {
|
2014-09-30 09:41:28 +04:00
|
|
|
nbfields = array_len(ORIGHEADERS)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (int(field) == field) {
|
|
|
|
field = int(field)
|
|
|
|
if (field >= 1 && field <= nbfields) return field
|
|
|
|
else return 0
|
|
|
|
}
|
2014-08-18 11:25:57 +04:00
|
|
|
field = tolower(field)
|
2013-08-27 15:14:44 +04:00
|
|
|
for (i = 1; i <= nbfields; i++) {
|
2014-08-18 11:25:57 +04:00
|
|
|
if (field == tolower(ORIGHEADERS[i])) {
|
2013-08-27 15:14:44 +04:00
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function ogeti(num) { if (num != 0) return ORIGFIELDS[num] }
|
|
|
|
function oget(field) { return ogeti(ogeth(field)) }
|
|
|
|
|
|
|
|
function geth(field, nbfields, i) {
|
2014-09-30 09:41:28 +04:00
|
|
|
nbfields = array_len(HEADERS)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (int(field) == field) {
|
|
|
|
field = int(field)
|
|
|
|
if (field >= 1 && field <= nbfields) return field
|
|
|
|
else return 0
|
|
|
|
}
|
|
|
|
field = tolower(field)
|
|
|
|
for (i = 1; i <= nbfields; i++) {
|
|
|
|
if (field == tolower(HEADERS[i])) {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function sethi(num, value) { if (num != 0) HEADERS[num] = value }
|
|
|
|
function seth(field, value) { sethi(geth(field), value) }
|
|
|
|
function addh(field, num) {
|
|
|
|
num = geth(field)
|
|
|
|
if (num == 0) {
|
2014-09-30 09:41:28 +04:00
|
|
|
num = array_len(HEADERS) + 1
|
2013-08-27 15:14:44 +04:00
|
|
|
HEADERS[num] = field
|
|
|
|
}
|
|
|
|
return num
|
|
|
|
}
|
|
|
|
function delhi(num, i, l) {
|
|
|
|
if (num == 0) return
|
|
|
|
array_deli(HEADERS, num)
|
|
|
|
}
|
|
|
|
function delh(field) { delhi(geth(field)) }
|
|
|
|
function geti(num) { if (num != 0) return $num }
|
|
|
|
function get(field) { return geti(geth(field)) }
|
|
|
|
function seti(num, value) { if (num != 0) $num = value }
|
|
|
|
function set(field, value) { seti(geth(field), value) }
|
|
|
|
function add(field, value, num, i, max) {
|
|
|
|
num = addh(field)
|
|
|
|
i = NF
|
2014-09-30 09:41:28 +04:00
|
|
|
max = array_len(HEADERS)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (i < max) {
|
|
|
|
i = i + 1
|
|
|
|
while(i <= max) {
|
|
|
|
$i = ""
|
|
|
|
i = i + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($num == "") {
|
|
|
|
$num = value
|
|
|
|
} else {
|
|
|
|
$num = $num ";" value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function deli(num, i, j) {
|
|
|
|
if (num == 0) return
|
|
|
|
i = num
|
|
|
|
while (i < NF) {
|
|
|
|
j = i + 1
|
|
|
|
$i = $j
|
|
|
|
i = i + 1
|
|
|
|
}
|
|
|
|
NF = NF - 1
|
|
|
|
}
|
|
|
|
function del(field) { deli(geth(field)) }
|
|
|
|
|
2020-02-13 09:04:26 +04:00
|
|
|
function __starts_with(prefix, string) {
|
|
|
|
return substr(string, 1, length(prefix)) == prefix
|
|
|
|
}
|
|
|
|
function __comparevic_truth(retval) {
|
|
|
|
return substr(retval, 1, 1) == "1"
|
|
|
|
}
|
|
|
|
function __comparevic_suffix(retval) {
|
|
|
|
return substr(retval, 2)
|
|
|
|
}
|
|
|
|
function comparevic(field1, value1, field2, value2, icfields, spfields, array, i, vs, f, prefix, suffix) {
|
|
|
|
# tester si on est un champ spécial.
|
|
|
|
prefix = ""
|
|
|
|
split(spfields, array, /,/)
|
|
|
|
for (i in array) {
|
|
|
|
split(array[i], vs, /=/)
|
|
|
|
f = tolower(vs[1])
|
|
|
|
if (f == tolower(field1) || f == tolower(field2)) {
|
|
|
|
prefix = vs[2]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
suffix = ""
|
|
|
|
if (prefix != "") {
|
|
|
|
# les valeurs ayant le préfixe spécifié sont ignorées
|
|
|
|
split(value1, array, /;/)
|
|
|
|
array_new(vs)
|
|
|
|
for (i in array) {
|
|
|
|
if (!__starts_with(prefix, array[i])) {
|
|
|
|
array_add(vs, array[i])
|
|
|
|
} else {
|
|
|
|
if (suffix != "") suffix = suffix ";"
|
|
|
|
suffix = suffix array[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value1 = array_join(vs, ";")
|
|
|
|
split(value2, array, /;/)
|
|
|
|
array_new(vs)
|
|
|
|
for (i in array) {
|
|
|
|
if (!__starts_with(prefix, array[i])) {
|
|
|
|
array_add(vs, array[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value2 = array_join(vs, ";")
|
|
|
|
}
|
2013-08-27 15:14:44 +04:00
|
|
|
split(icfields, array, /,/)
|
|
|
|
if (in_array(field1, array, 1) || in_array(field2, array, 1)) {
|
2020-02-13 09:04:26 +04:00
|
|
|
i = tolower(value1) == tolower(value2)? "1": "0"
|
2013-08-27 15:14:44 +04:00
|
|
|
} else {
|
2020-02-13 09:04:26 +04:00
|
|
|
i = value1 == value2? "1": "0"
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
2020-02-13 09:04:26 +04:00
|
|
|
return i suffix
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
2020-02-13 09:04:26 +04:00
|
|
|
function ocompareic(field1, field2, icfields, spfields, v1, v2, array) {
|
|
|
|
return comparevic(field1, oget(field1), field2, oget(field2), icfields, spfields)
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
2020-02-13 09:04:26 +04:00
|
|
|
function compareic(field1, field2, icfields, spfields, v1, v2, array) {
|
|
|
|
return comparevic(field1, get(field1), field2, get(field2), icfields, spfields)
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
|
|
|
function infields(field, fields, array) {
|
|
|
|
split(fields, array, /,/)
|
|
|
|
return in_array(field, array, 1)
|
|
|
|
}
|
|
|
|
function parseheaders() {
|
|
|
|
if (do_once("parse-headers")) {
|
|
|
|
array_parsecsv(ORIGHEADERS, $0)
|
|
|
|
if (!user_headers_count) array_copy(HEADERS, ORIGHEADERS)
|
|
|
|
next
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function printheaders() {
|
|
|
|
if (do_once("show-headers")) {
|
|
|
|
array_printcsv(HEADERS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function resetheaders() {
|
|
|
|
array_new(HEADERS)
|
|
|
|
}
|
|
|
|
function resetfields( nf) {
|
|
|
|
$0 = ""
|
2014-09-30 09:41:28 +04:00
|
|
|
nf = array_len(HEADERS)
|
2013-08-27 15:14:44 +04:00
|
|
|
$nf = ""
|
|
|
|
}
|
|
|
|
function copyfield(field) {
|
|
|
|
set(field, oget(field))
|
|
|
|
}
|
|
|
|
function copyfields(fields, array) {
|
|
|
|
split(fields, array, /,/)
|
2014-09-30 09:41:28 +04:00
|
|
|
for (i = 1; i <= array_len(array); i++) {
|
2013-08-27 15:14:44 +04:00
|
|
|
copyfield(array[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function copyall() {
|
|
|
|
array_getline(ORIGFIELDS)
|
|
|
|
}
|
|
|
|
function checkfields(fields, missings, array, r, field) {
|
|
|
|
array_new(missings)
|
|
|
|
split(fields, array, /,/)
|
|
|
|
r = 1
|
2014-09-30 09:41:28 +04:00
|
|
|
for (i = 1; i <= array_len(array); i++) {
|
2013-08-27 15:14:44 +04:00
|
|
|
field = array[i]
|
|
|
|
if (geth(field) == 0) {
|
|
|
|
array_add(missings, field)
|
|
|
|
r = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
function checkvalues(fields, missings, array, r, field) {
|
|
|
|
array_new(missings)
|
|
|
|
split(fields, array, /,/)
|
|
|
|
r = 1
|
2014-09-30 09:41:28 +04:00
|
|
|
for (i = 1; i <= array_len(array); i++) {
|
2013-08-27 15:14:44 +04:00
|
|
|
field = array[i]
|
|
|
|
if (geth(field) == 0 || !get(field)) {
|
|
|
|
array_add(missings, field)
|
|
|
|
r = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
function mapfields(fieldmap, mapitems, parts) {
|
|
|
|
split(fieldmap, mapitems, /,/)
|
2014-09-30 09:41:28 +04:00
|
|
|
for (i = 1; i <= array_len(mapitems); i++) {
|
2013-08-27 15:14:44 +04:00
|
|
|
split(mapitems[i], parts, /:/)
|
2014-09-30 09:41:28 +04:00
|
|
|
if (array_len(parts) != 2) continue
|
2013-08-27 15:14:44 +04:00
|
|
|
desti = geth(parts[1])
|
|
|
|
srci = geth(parts[2])
|
|
|
|
if (desti == 0 && srci != 0) {
|
|
|
|
HEADERS[srci] = parts[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function build_skipfs(keepfields, skipfields, skipfs, addfs, keepfs, keepfs_count, tmpfields, headers_count, i, field, fieldi) {
|
|
|
|
array_new(skipfs)
|
|
|
|
array_new(addfs)
|
|
|
|
if (skipfields == "*") { skipfields = ""; keepfields = "" }
|
|
|
|
if (keepfields == "*" && skipfields == "") return 0
|
|
|
|
# construire la liste des champs à garder dans keepfs
|
|
|
|
split(keepfields, keepfs, /,/)
|
|
|
|
if (in_array("*", keepfs)) {
|
|
|
|
array_del(keepfs, "*")
|
|
|
|
array_extend(keepfs, HEADERS)
|
|
|
|
}
|
|
|
|
split(skipfields, tmpfields, /,/)
|
|
|
|
for (i in tmpfields) {
|
2014-08-18 11:25:57 +04:00
|
|
|
array_del(keepfs, tmpfields[i], 1)
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
2014-09-30 09:41:28 +04:00
|
|
|
keepfs_count = array_len(keepfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
# puis construire la liste des champs à supprimer dans skipfs
|
2014-09-30 09:41:28 +04:00
|
|
|
headers_count = array_len(HEADERS)
|
2013-08-27 15:14:44 +04:00
|
|
|
for (i = 1; i <= headers_count; i++) {
|
|
|
|
field = HEADERS[i]
|
2014-08-18 11:25:57 +04:00
|
|
|
if (!in_array(field, keepfs, 1)) {
|
2013-08-27 15:14:44 +04:00
|
|
|
fieldi = geth(field)
|
|
|
|
if (i != 0) array_add(skipfs, fieldi)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
asort(skipfs)
|
|
|
|
# puis construire la liste des champs à ajouter dans addfs
|
|
|
|
for (i = 1; i <= keepfs_count; i++) {
|
|
|
|
field = keepfs[i]
|
2014-08-18 11:25:57 +04:00
|
|
|
if (!in_array(field, HEADERS, 1)) {
|
2013-08-27 15:14:44 +04:00
|
|
|
array_add(addfs, field)
|
|
|
|
}
|
|
|
|
}
|
2014-09-30 09:41:28 +04:00
|
|
|
return array_len(skipfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
|
|
|
function filterheaders(skipfs, addfs, skipfs_count, addfs_count) {
|
2014-09-30 09:41:28 +04:00
|
|
|
skipfs_count = array_len(skipfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
for (i = skipfs_count; i >= 1; i--) {
|
|
|
|
array_deli(HEADERS, skipfs[i])
|
|
|
|
}
|
2014-09-30 09:41:28 +04:00
|
|
|
addfs_count = array_len(addfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
for (i = 1; i <= addfs_count; i++) {
|
|
|
|
addh(addfs[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function filterfields(skipfs, addfs, skipfs_count) {
|
2014-09-30 09:41:28 +04:00
|
|
|
skipfs_count = array_len(skipfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
for (i = skipfs_count; i >= 1; i--) {
|
|
|
|
deli(skipfs[i])
|
|
|
|
}
|
2014-09-30 09:41:28 +04:00
|
|
|
addfs_count = array_len(addfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
for (i = 1; i <= addfs_count; i++) {
|
|
|
|
add(addfs[i], "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'
|
|
|
|
function lawkcsv() {
|
|
|
|
local beforescript='{
|
|
|
|
if (user_headers_count && do_once("user-headers")) {
|
|
|
|
if (!parse_headers) array_copy(ORIGHEADERS, user_headers)
|
|
|
|
array_copy(HEADERS, user_headers)
|
|
|
|
}
|
|
|
|
if (parse_headers) parseheaders()
|
|
|
|
}'
|
|
|
|
local append_beforescript=
|
|
|
|
local awkscript='{
|
2014-09-30 09:41:28 +04:00
|
|
|
array_parsecsv(ORIGFIELDS, $0, array_len(ORIGHEADERS))
|
2013-08-27 15:14:44 +04:00
|
|
|
if (reset_fields) { resetfields() } else { copyall() }
|
|
|
|
}'
|
|
|
|
local append_awkscript=
|
|
|
|
local afterscript=
|
|
|
|
local append_afterscript='{
|
|
|
|
if (show_headers) printheaders()
|
|
|
|
printcsv()
|
|
|
|
}'
|
|
|
|
|
|
|
|
local -a args headers vars
|
2016-02-29 22:01:49 +04:00
|
|
|
local skip=0 parse_headers=
|
2013-08-27 15:14:44 +04:00
|
|
|
local autosep=
|
|
|
|
local sepconf=',"' colsep=',' qchar='"' echar=
|
|
|
|
local show_headers=1 reset_fields=
|
2016-02-29 22:01:49 +04:00
|
|
|
local fieldmap checkfields checkvalues keepf skipf
|
2013-08-27 15:14:44 +04:00
|
|
|
if parse_opts \
|
2016-02-29 23:27:21 +04:00
|
|
|
-s:,-S:,--skip-lines:,--skiplines: skip= \
|
|
|
|
-h,-H,--parse-headers parse_headers=1 \
|
2013-08-27 15:14:44 +04:00
|
|
|
--sepconf: '$autosep=sepconf; set@ sepconf' \
|
|
|
|
--colsep: '$autosep=indiv; set@ colsep' \
|
|
|
|
--qchar: '$autosep=indiv; set@ qchar' \
|
|
|
|
--echar: '$autosep=indiv; set@ echar' \
|
|
|
|
-n,--no-headers show_headers= \
|
|
|
|
--show-headers show_headers=1 \
|
|
|
|
-z,--reset-fields reset_fields=1 \
|
|
|
|
-v:,--var: vars \
|
|
|
|
--rb:,--rbe: '$set@ beforescript; append_beforescript=' \
|
|
|
|
-b:,--b:,--be:,--before-script: append_beforescript= \
|
|
|
|
--re: '$set@ awkscript; append_awkscript=' \
|
|
|
|
-e:,--e:,--awk-script:,--script: append_awkscript= \
|
|
|
|
-m:,--map:,--mapfields fieldmap= \
|
2016-02-29 22:01:49 +04:00
|
|
|
-c:,--check-fields:,--checkfields: checkfields= \
|
|
|
|
--check-values:,--checkvalues: checkvalues= \
|
|
|
|
-k:,--keep:,--keep-fields:,--keepfields: keepf= \
|
|
|
|
--skip:,--skip-fields:,--skipfields: skipf= \
|
2013-08-27 15:14:44 +04:00
|
|
|
--ra:,--rae: '$set@ afterscript; append_afterscript=' \
|
|
|
|
-a:,--a:,--ae:,--after-script: append_afterscript= \
|
|
|
|
@ args -- "$@"; then
|
|
|
|
set -- "${args[@]}"
|
|
|
|
else
|
|
|
|
eerror "$args"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ "$autosep" == sepconf ]; then
|
|
|
|
colsep="${sepconf:0:1}"
|
|
|
|
qchar="${sepconf:1:1}"
|
|
|
|
echar="${sepconf:2:1}"
|
|
|
|
autosep=
|
|
|
|
if [ -n "$colsep" -o -n "$qchar" -o -n "$echar" ]; then
|
|
|
|
[ -n "$colsep" ] || colsep=','
|
|
|
|
[ -n "$qchar" ] || qchar='"'
|
|
|
|
# n'activer la configuration que si elle diffère de la configuration
|
|
|
|
# par défaut
|
|
|
|
[ "$colsep" != ',' -o "$qchar" != '"' -o -n "$echar" ] && autosep=1
|
|
|
|
fi
|
|
|
|
elif [ "$autosep" == indiv ]; then
|
|
|
|
autosep=1
|
|
|
|
fi
|
|
|
|
colsep="${colsep:0:1}"
|
|
|
|
qchar="${qchar:0:1}"
|
|
|
|
echar="${echar:0:1}"
|
|
|
|
|
2014-01-21 11:51:02 +04:00
|
|
|
headers=()
|
|
|
|
while [ $# -gt 0 -a "$1" != "--" ]; do
|
|
|
|
array_add headers "$1"
|
|
|
|
shift
|
|
|
|
done
|
|
|
|
shift
|
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
[ -n "${headers[*]}" ] || parse_headers=1
|
2014-01-21 11:51:02 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
if [ -n "$fieldmap" ]; then
|
|
|
|
awkscript="$awkscript"'{
|
|
|
|
if (do_once("mapfields")) { mapfields(fields2map) }
|
|
|
|
}'
|
|
|
|
reset_fields=
|
|
|
|
fi
|
|
|
|
if [ -n "$checkfields" ]; then
|
|
|
|
awkscript="$awkscript"'{
|
|
|
|
if (do_once("checkfields")) { if (!checkfields(fields2check)) exit 1 }
|
|
|
|
}'
|
|
|
|
reset_fields=
|
|
|
|
fi
|
|
|
|
if [ -n "$checkvalues" ]; then
|
|
|
|
awkscript="$awkscript"'{
|
|
|
|
if (!checkvalues(values2check)) exit 1
|
|
|
|
}'
|
|
|
|
reset_fields=
|
|
|
|
fi
|
2016-02-29 22:01:49 +04:00
|
|
|
if [ -n "$skipf" ]; then
|
|
|
|
[ -n "$keepf" ] || keepf="*"
|
2013-08-27 15:14:44 +04:00
|
|
|
fi
|
2016-02-29 22:01:49 +04:00
|
|
|
if [ -n "$keepf" ]; then
|
2013-08-27 15:14:44 +04:00
|
|
|
awkscript="$awkscript"'{
|
|
|
|
if (do_once("filterfields")) {
|
|
|
|
build_skipfs(fields2keep, fields2skip, SKIPFS, ADDFS)
|
|
|
|
filterheaders(SKIPFS, ADDFS)
|
|
|
|
}
|
|
|
|
filterfields(SKIPFS, ADDFS)
|
|
|
|
}'
|
|
|
|
reset_fields=
|
|
|
|
fi
|
|
|
|
|
2017-08-24 12:15:16 +04:00
|
|
|
lawkrun -f \
|
2016-02-29 22:01:49 +04:00
|
|
|
skip_lines:int="$skip" parse_headers:int="$parse_headers" \
|
2013-08-27 15:14:44 +04:00
|
|
|
autosep:int="$autosep" colsep="$colsep" qchar="$qchar" echar="$echar" \
|
|
|
|
show_headers:int="$show_headers" reset_fields:int="$reset_fields" \
|
|
|
|
fields2map="$fieldmap" fields2check="$checkfields" values2check="$checkvalues" \
|
2016-02-29 22:01:49 +04:00
|
|
|
fields2keep="$keepf" fields2skip="$skipf" \
|
2013-08-27 15:14:44 +04:00
|
|
|
"user_headers[@]=headers" "${vars[@]}" \
|
|
|
|
"$__AWKCSV_FUNCTIONS"'
|
|
|
|
BEGIN {
|
|
|
|
if (autosep) {
|
|
|
|
DEFAULT_COLSEP = colsep
|
|
|
|
DEFAULT_QCHAR = qchar
|
|
|
|
DEFAULT_ECHAR = echar
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NR <= skip_lines { next }
|
|
|
|
'"$beforescript
|
|
|
|
$append_beforescript
|
|
|
|
$awkscript
|
|
|
|
$append_awkscript
|
|
|
|
$afterscript
|
|
|
|
$append_afterscript
|
2014-01-21 11:51:02 +04:00
|
|
|
" -- "$@"
|
2013-08-27 15:14:44 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function cawkcsv() { LANG=C lawkcsv "$@"; }
|
|
|
|
function awkcsv() { LANG=C lawkcsv "$@"; }
|
|
|
|
|
2014-01-21 11:35:07 +04:00
|
|
|
################################################################################
|
|
|
|
|
2014-03-03 11:24:17 +04:00
|
|
|
__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:
|
2016-02-29 23:27:21 +04:00
|
|
|
-s, -S, --skip-lines nblines
|
2014-03-03 11:24:17 +04:00
|
|
|
Sauter nblines au début du flux
|
2016-02-29 23:27:21 +04:00
|
|
|
-h, -H, --parse-headers
|
2014-03-03 11:24:17 +04:00
|
|
|
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
|
2016-02-29 22:01:49 +04:00
|
|
|
local skip= parse_headers=
|
2014-03-03 11:24:17 +04:00
|
|
|
local sepconf= colsep= qchar= echar=
|
|
|
|
local no_headers= quiet=
|
|
|
|
local awkscript=--use-default--
|
|
|
|
local append_awkscript=
|
|
|
|
if parse_opts \
|
2016-02-29 23:27:21 +04:00
|
|
|
-s:,-S:,--skip:,--skip-lines:,--skiplines: skip= \
|
|
|
|
-h,-H,--parse-headers parse_headers=1 \
|
2014-03-03 11:24:17 +04:00
|
|
|
--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
|
2017-08-24 12:15:16 +04:00
|
|
|
array_from_lines headers "$(lawkrun -f lskip:int="$skip" 'NR <= lskip { next }
|
2014-03-03 11:24:17 +04:00
|
|
|
{
|
|
|
|
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 }"
|
|
|
|
|
2016-02-29 22:01:49 +04:00
|
|
|
lawkcsv ${skip:+-s "$skip"} ${parse_headers:+-h} \
|
2014-03-03 11:24:17 +04:00
|
|
|
${sepconf:+--sepconf "$sepconf"} ${colsep:+--colsep "$colsep"} ${qchar:+--qchar "$qchar"} ${echar:+--echar "$echar"} \
|
|
|
|
${no_headers:+--no-headers} -v quiet:int=$quiet \
|
2014-09-30 09:41:28 +04:00
|
|
|
--re '{array_parsecsv(ORIGFIELDS, $0, array_len(ORIGHEADERS))}' -a "$grepscript" \
|
2014-03-03 11:24:17 +04:00
|
|
|
-- "${headers[@]}" -- "${inputfiles[@]}"
|
|
|
|
local r=$?
|
|
|
|
|
|
|
|
ac_clean "${tmpfiles[@]}"
|
|
|
|
return $r
|
|
|
|
}
|
|
|
|
|
|
|
|
function cgrepcsv() { LANG=C lgrepcsv "$@"; }
|
|
|
|
function grepcsv() { LANG=C lgrepcsv "$@"; }
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
|
2014-01-21 11:35:07 +04:00
|
|
|
__AWKFSV2CSV_HELP="\
|
|
|
|
Transformer un flux fsv (colonnes à largeurs fixes) en csv
|
|
|
|
|
2014-01-21 12:18:59 +04:00
|
|
|
Chaque argument doit être de la forme [-]header:size. La colonne sera incluse
|
|
|
|
dans le fichier en sortie, sauf si elle est précédée de -
|
|
|
|
|
2014-01-21 11:35:07 +04:00
|
|
|
Analyse du flux en entrée:
|
2016-02-29 23:27:21 +04:00
|
|
|
-s, -S, --skip-lines nblines
|
2014-01-21 11:35:07 +04:00
|
|
|
Sauter nblines au début du flux
|
2014-01-21 12:18:59 +04:00
|
|
|
-r, --no-trim
|
|
|
|
Ne pas trimmer les valeurs à droite.
|
2014-01-21 11:35:07 +04:00
|
|
|
|
|
|
|
Flux en sortie:
|
|
|
|
-n, --no-headers
|
|
|
|
Ne pas afficher les en-têtes. Par défaut, les en-têtes sont affichés."
|
|
|
|
|
|
|
|
function lawkfsv2csv() {
|
|
|
|
local -a args headersizes
|
2016-02-29 22:01:49 +04:00
|
|
|
local skip=0 rtrim=1 show_headers=1
|
2014-01-21 11:35:07 +04:00
|
|
|
if parse_opts \
|
2016-02-29 23:27:21 +04:00
|
|
|
-s:,-S:,--skip:,--skip-lines:,--skiplines: skip= \
|
2016-02-29 22:01:49 +04:00
|
|
|
-r,--no-trim rtrim= \
|
2014-01-21 11:35:07 +04:00
|
|
|
-n,--no-headers show_headers= \
|
|
|
|
--show-headers show_headers=1 \
|
|
|
|
@ args -- "$@"; then
|
|
|
|
set -- "${args[@]}"
|
|
|
|
else
|
|
|
|
eerror "$args"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
local -a headers starts sizes
|
|
|
|
local headersize header i size
|
|
|
|
i=1
|
2014-01-21 11:51:02 +04:00
|
|
|
while [ $# -gt 0 -a "$1" != "--" ]; do
|
|
|
|
headersize="$1"
|
|
|
|
shift
|
|
|
|
|
2014-01-21 11:35:07 +04:00
|
|
|
splitpair "$headersize" header size
|
|
|
|
[ -n "$header" ] || {
|
|
|
|
eerror "header est requis"
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
[ -n "$size" ] || size=1
|
|
|
|
if [ "${header#-}" == "$header" ]; then
|
|
|
|
array_add headers "$header"
|
|
|
|
array_add starts "$i"
|
|
|
|
array_add sizes "$size"
|
|
|
|
fi
|
|
|
|
i="$(($i + $size))"
|
|
|
|
done
|
2014-01-21 11:51:02 +04:00
|
|
|
shift
|
2014-01-21 11:35:07 +04:00
|
|
|
|
2017-08-24 12:15:16 +04:00
|
|
|
lawkrun -f \
|
2016-02-29 22:01:49 +04:00
|
|
|
skip_lines:int="$skip" trim_values:int="$rtrim" show_headers:int="$show_headers" \
|
2014-01-21 11:35:07 +04:00
|
|
|
headers[@] starts[@] sizes[@] \
|
|
|
|
"$__AWKCSV_FUNCTIONS"'
|
|
|
|
BEGIN {
|
|
|
|
if (show_headers) {
|
|
|
|
print array_formatcsv(headers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NR <= skip_lines { next }
|
|
|
|
{
|
|
|
|
line = $0
|
|
|
|
$0 = ""
|
2014-01-21 12:18:59 +04:00
|
|
|
for (i = 1; i <= headers_count; i++) {
|
|
|
|
value = substr(line, starts[i], sizes[i])
|
2014-01-21 12:21:02 +04:00
|
|
|
if (trim_values) {
|
|
|
|
sub(/^ */, "", value)
|
|
|
|
sub(/ *$/, "", value)
|
|
|
|
}
|
2014-01-21 12:18:59 +04:00
|
|
|
$i = value
|
2014-01-21 11:35:07 +04:00
|
|
|
}
|
|
|
|
formatcsv()
|
|
|
|
print
|
|
|
|
}
|
2014-01-21 11:51:02 +04:00
|
|
|
' -- "$@"
|
2014-01-21 11:35:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function cawkfsv2csv() { LANG=C lawkfsv2csv "$@"; }
|
|
|
|
function awkfsv2csv() { LANG=C lawkfsv2csv "$@"; }
|
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
################################################################################
|
|
|
|
|
2017-08-24 12:15:16 +04:00
|
|
|
__AWKCSV2FSV_HELP="\
|
|
|
|
Transformer un flux csv en fsv (colonnes à largeurs fixes)
|
|
|
|
|
|
|
|
Chaque argument doit être de la forme field:size"
|
|
|
|
|
|
|
|
function lawkcsv2fsv() {
|
|
|
|
local -a args fieldsizes
|
|
|
|
if parse_opts @ args -- "$@"; then
|
|
|
|
set -- "${args[@]}"
|
|
|
|
else
|
|
|
|
eerror "$args"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
local -a fields sizes
|
|
|
|
local fieldsize field size
|
|
|
|
while [ $# -gt 0 -a "$1" != "--" ]; do
|
|
|
|
fieldsize="$1"
|
|
|
|
shift
|
|
|
|
|
|
|
|
splitpair "$fieldsize" field size
|
|
|
|
[ -n "$field" ] || {
|
|
|
|
eerror "field est requis"
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
[ -n "$size" ] || size=1
|
|
|
|
array_add fields "$field"
|
|
|
|
array_add sizes "$size"
|
|
|
|
done
|
|
|
|
shift
|
|
|
|
|
2017-08-25 15:45:39 +04:00
|
|
|
lawkcsv -v fields[@] -v sizes[@] -a '{
|
2017-08-24 12:15:16 +04:00
|
|
|
line = ""
|
|
|
|
for (i = 1; i <= fields_count; i++) {
|
|
|
|
size = sizes[i] + 0
|
2017-08-25 15:45:39 +04:00
|
|
|
value = get(fields[i]) ""
|
2017-08-24 12:15:16 +04:00
|
|
|
while (length(value) < size) value = value " "
|
|
|
|
if (length(value) > size) value = substr(value, 1, size)
|
|
|
|
line = line value
|
|
|
|
}
|
|
|
|
print line
|
2017-08-25 15:45:39 +04:00
|
|
|
}' -- "$@"
|
2017-08-24 12:15:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function cawkcsv2fsv() { LANG=C lawkcsv2fsv "$@"; }
|
|
|
|
function awkcsv2fsv() { LANG=C lawkcsv2fsv "$@"; }
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
__MERGECSV_HELP="\
|
|
|
|
Fusionner deux fichiers csv en faisant la correspondance sur la valeur d'un
|
|
|
|
champ, qui est la clé
|
|
|
|
|
2016-02-29 23:27:21 +04:00
|
|
|
-h, -H, --parse-headers
|
2013-08-27 15:14:44 +04:00
|
|
|
Lire la liste des champs à partir de la première ligne non ignorée des flux.
|
|
|
|
Si cette option est spécifiée (ce qui est le cas par défaut), les champs
|
|
|
|
spécifiés avec les options -k, --lk et --rk peuvent être les noms effectifs
|
|
|
|
des champs. Sinon, les champs ne peuvent être que numériques.
|
2016-02-29 23:27:21 +04:00
|
|
|
-n, -N, --numkeys
|
2013-08-27 15:14:44 +04:00
|
|
|
Ne pas analyser la première ligne pour les noms des champs. Les champs
|
|
|
|
spécifiés ne peuvent être que numériques.
|
|
|
|
--lskip nblines
|
|
|
|
--rskip nblines
|
|
|
|
Sauter nblines au début du flux, respectivement pour left et pour right
|
|
|
|
--lheaders FIELDS
|
|
|
|
--rheaders FIELDS
|
|
|
|
Spécifier les noms des champs à utiliser *en sortie*, respectivement pour
|
|
|
|
left et pour right. Les champs doivent être séparés par des virgules.
|
|
|
|
Si ces valeurs ne sont pas spécifiées, prendre les champs lus avec l'option
|
|
|
|
--parse-headers. Sinon, ne pas afficher d'en-têtes en sortie.
|
|
|
|
--lprefix prefix
|
|
|
|
--rprefix prefix
|
|
|
|
Spécifier un préfixe à rajouter automatiquement aux champs à utiliser *en
|
|
|
|
sortie*. Le prefix est rajouté que les champs soient déterminés par
|
|
|
|
--parse-headers ou spécifiés par --{l,r}headers. La correspondance des
|
|
|
|
champs avec --lk et --rk est faite sur les noms des champs *avant* qu'ils
|
|
|
|
soient modifiés par cette option.
|
|
|
|
--lk, --lkey FIELD
|
|
|
|
--rk, --rkey FIELD
|
|
|
|
Spécifier le champ utilisé pour faire la correspondance entre les deux flux,
|
|
|
|
respectivement pour left et pour right.
|
|
|
|
Si --parse-headers n'est pas spécifié, ces valeurs doivent être numériques.
|
|
|
|
La valeur par défaut est 1.
|
|
|
|
-k, --key FIELD
|
|
|
|
Spécifier le champ utilisé pour faire la correspondance sur les deux
|
|
|
|
flux. Equivalent à '--lk FIELD --rk FIELD'
|
|
|
|
La correspondance des champs avec --lk et --rk est faite sur les noms des
|
|
|
|
champs *avant* qu'ils soient modifiés le cas échéant par --lprefix et/ou
|
|
|
|
--rprefix
|
|
|
|
-i
|
|
|
|
Faire la correspondance de la valeur des champs sans tenir compte de la
|
|
|
|
casse
|
|
|
|
|
|
|
|
Après la fusion des deux fichiers csv, il est possible de faire une sélection
|
|
|
|
des lignes selon certains critères:
|
|
|
|
|
|
|
|
-s SELECT
|
|
|
|
Spécifier un type de sélection à faire. SELECT peut valoir:
|
|
|
|
none, n
|
|
|
|
C'est l'action par défaut; toutes les lignes sont affichées
|
|
|
|
inner-join, j
|
|
|
|
N'inclure dans le résultat que les lignes pour lesquelles il y a une
|
|
|
|
correspondance sur les clés.
|
|
|
|
L'option -J est synonyme de '-s inner-join'
|
|
|
|
left-join, l
|
|
|
|
Inclure dans le résultat les lignes pour lesquelles il y a une
|
|
|
|
correspondance sur les clés, et les lignes de left pour lesquelles il
|
|
|
|
n'y a pas de correspondance. Les lignes de right sans correspondance ne
|
|
|
|
sont pas affichées.
|
|
|
|
L'option -L est synonyme de '-s left-join'
|
|
|
|
right-join, r
|
|
|
|
Inclure dans le résultat les lignes pour lesquelles il y a une
|
|
|
|
correspondance sur les clés, et les lignes de right pour lesquelles il
|
|
|
|
n'y a pas de correspondance. Les lignes de left sans correspondance ne
|
|
|
|
sont pas affichées.
|
|
|
|
L'option -R est synonyme de '-s right-join'
|
|
|
|
left-only, lo
|
|
|
|
N'inclure dans le résultat que les lignes de left pour lesquelles il n'y
|
|
|
|
a pas de correspondance dans right.
|
|
|
|
right-only, ro
|
|
|
|
N'inclure dans le résultat que les lignes de right pour lesquelles il n'y
|
|
|
|
a pas de correspondance dans left.
|
|
|
|
|
|
|
|
Les options suivantes sont des actions qu'il est possible d'effectuer après la
|
|
|
|
sélection des lignes. Les noms des champs spécifiés dans ces options sont les
|
|
|
|
noms en sortie, en tenant compte de l'analyse et du traitement effectués par les
|
|
|
|
options --parse-headers, --{l,r}headers et --{l,r}prefix.
|
|
|
|
|
|
|
|
--copy, --lcopyf FIELDS
|
|
|
|
Copier les champs spécifiés de right vers left. Les champs spécifiés sont
|
|
|
|
séparés par des virgules et sont de la forme [dest:]src
|
|
|
|
--rcopyf FIELDS
|
|
|
|
Copier les champs spécifiés de left vers right. Les champs spécifiés sont
|
|
|
|
séparés par des virgules et sont de la forme [dest:]src
|
|
|
|
--lkeepf FIELDS
|
|
|
|
Garder les champs de left spécifiés. Les autres sont supprimés de la sortie.
|
|
|
|
FIELDS est une liste de champs séparés par des virgules, ou '*' pour
|
|
|
|
spécifier de garder tous les champs ou '' pour ne garder aucun champ.
|
|
|
|
Par défaut, avec '-s right-only', FIELDS vaut '', sinon FIELDS vaut '*'
|
|
|
|
--keep, --rkeepf FIELDS
|
|
|
|
Garder les champs de right spécifiés. Les autres sont supprimés de la
|
|
|
|
sortie. FIELDS est une liste de champs séparés par des virgules, ou '*' pour
|
|
|
|
spécifier de garder tous les champs, ou '' pour ne garder aucun champ.
|
|
|
|
Par défaut, avec '-s left-only', FIELDS vaut '', sinon FIELDS vaut '*'
|
|
|
|
--lskipf FIELDS
|
|
|
|
--rskipf FIELDS
|
|
|
|
Exclure les champs de left (resp. de right) spécifiés. FIELDS est une liste
|
|
|
|
de champs séparés par des virgules."
|
|
|
|
|
|
|
|
: "${__MERGECSV_DEBUG:=}"
|
|
|
|
function lmergecsv() {
|
|
|
|
# Fusionner sur la sortie standard les deux fichiers csv $1 et $2. La clé du
|
|
|
|
# fichier $1 est spécifiée par l'option --lkey et vaut 1 par défaut. La clé
|
|
|
|
# du fichier $2 est spécifiée par l'option --rkey et vaut 1 par défaut. Les
|
|
|
|
# valeurs des clés ne doivent pas faire plus de 64 caractères de long.
|
|
|
|
eval "$(utools_local)"
|
|
|
|
local parse_headers=auto
|
|
|
|
local ignore_case=
|
|
|
|
local lskip=0 lkey=1 lheaders= lprefix=
|
|
|
|
local rskip=0 rkey=1 rheaders= rprefix=
|
|
|
|
local select=none
|
|
|
|
|
|
|
|
local postproc=auto
|
|
|
|
local lcopyf= rcopyf=
|
|
|
|
local lkeepf=--NOT-SET-- rkeepf=--NOT-SET--
|
|
|
|
local lskipf= rskipf=
|
|
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
2016-02-29 23:27:21 +04:00
|
|
|
-h,-H,--parse-headers parse_headers=1 \
|
|
|
|
-n,-N,--numkeys parse_headers= \
|
2013-08-27 15:14:44 +04:00
|
|
|
--lskip: lskip= \
|
|
|
|
--lkey:,--lk: lkey= \
|
|
|
|
--lheaders:,--lh: lheaders= \
|
|
|
|
--lprefix:,--lp: lprefix= \
|
|
|
|
--rskip: rskip= \
|
|
|
|
--rkey:,--rk: rkey= \
|
|
|
|
--rheaders:,--rh: rheaders= \
|
|
|
|
--rprefix:,--rp: rprefix= \
|
|
|
|
-i,--ignore-case ignore_case=1 \
|
|
|
|
-k:,--key: '$set@ lkey; set@ rkey' \
|
|
|
|
-s:,--select: select= \
|
|
|
|
-J select=inner-join \
|
|
|
|
-L select=left-join \
|
|
|
|
-R select=right-join \
|
|
|
|
--lcopyf:,--copy: '$set@ lcopyf; postproc=1' \
|
|
|
|
--rcopyf: '$set@ rcopyf; postproc=1' \
|
|
|
|
--lkeepf: '$set@ lkeepf; postproc=1' \
|
|
|
|
--rkeepf:,--keep: '$set@ rkeepf; postproc=1' \
|
|
|
|
--lskipf: '$set@ lskipf; postproc=1' \
|
|
|
|
--rskipf: '$set@ rskipf; postproc=1' \
|
|
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
|
|
|
|
local lfile="$1"
|
|
|
|
local rfile="$2"
|
|
|
|
[ -f "$lfile" -a -f "$rfile" ] || {
|
|
|
|
[ -f "$lfile" ] || eerror "$lfile: fichier introuvable"
|
|
|
|
[ -f "$rfile" ] || eerror "$rfile: fichier introuvable"
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
local padding="----------------------------------------------------------------"
|
|
|
|
local padlen=${#padding}
|
|
|
|
|
|
|
|
[ "$parse_headers" == "auto" ] && parse_headers=1
|
|
|
|
if [ -n "$parse_headers" ]; then
|
2017-08-24 12:15:16 +04:00
|
|
|
[ -n "$lheaders" ] || lheaders="$(<"$lfile" lawkrun lskip:int="$lskip" 'NR <= lskip { next } { print; exit }')"
|
|
|
|
[ -n "$rheaders" ] || rheaders="$(<"$rfile" lawkrun lskip:int="$lskip" 'NR <= lskip { next } { print; exit }')"
|
2013-08-27 15:14:44 +04:00
|
|
|
fi
|
|
|
|
|
|
|
|
# faire le fichier de travail pour lfile
|
|
|
|
local tmpleft
|
|
|
|
ac_set_tmpfile tmpleft "" __mergecsv_left "" __MERGECSV_DEBUG
|
2017-08-24 12:15:16 +04:00
|
|
|
<"$lfile" lawkrun -f \
|
2013-08-27 15:14:44 +04:00
|
|
|
padding="$padding" padlen:int="$padlen" \
|
|
|
|
parse_headers:int="$parse_headers" ignore_case:int="$ignore_case" \
|
|
|
|
lskip:int="$lskip" lkey="$lkey" \
|
|
|
|
"$__AWKCSV_FUNCTIONS"'
|
|
|
|
NR <= lskip { next }
|
|
|
|
parse_headers && do_once("parse-headers") {
|
|
|
|
array_parsecsv(HEADERS, $0)
|
|
|
|
lkey = geth(lkey)
|
|
|
|
if (!lkey) lkey = 1
|
|
|
|
next
|
|
|
|
}
|
|
|
|
{
|
|
|
|
oline = $0
|
|
|
|
parsecsv($0)
|
|
|
|
keyvalue = substr($lkey, 1, padlen)
|
|
|
|
if (ignore_case) keyvalue = tolower(keyvalue)
|
|
|
|
print keyvalue substr(padding, 1, padlen - length(keyvalue)) "1" oline
|
|
|
|
}
|
|
|
|
' | csort -su >"$tmpleft"
|
|
|
|
|
|
|
|
# faire le fichier de travail pour rfile
|
|
|
|
local tmpright
|
|
|
|
ac_set_tmpfile tmpright "" __mergecsv_right "" __MERGECSV_DEBUG
|
2017-08-24 12:15:16 +04:00
|
|
|
<"$rfile" lawkrun -f \
|
2013-08-27 15:14:44 +04:00
|
|
|
padding="$padding" padlen:int="$padlen" \
|
|
|
|
parse_headers:int="$parse_headers" ignore_case:int="$ignore_case" \
|
|
|
|
rskip:int="$rskip" rkey="$rkey" \
|
|
|
|
"$__AWKCSV_FUNCTIONS"'
|
|
|
|
NR <= rskip { next }
|
|
|
|
parse_headers && do_once("parse-headers") {
|
|
|
|
array_parsecsv(HEADERS, $0)
|
|
|
|
rkey = geth(rkey)
|
|
|
|
if (!rkey) rkey = 1
|
|
|
|
next
|
|
|
|
}
|
|
|
|
{
|
|
|
|
oline = $0
|
|
|
|
parsecsv($0)
|
|
|
|
keyvalue = substr($rkey, 1, padlen)
|
|
|
|
if (ignore_case) keyvalue = tolower(keyvalue)
|
|
|
|
print keyvalue substr(padding, 1, padlen - length(keyvalue)) "2" oline
|
|
|
|
}
|
|
|
|
' | csort -su >"$tmpright"
|
|
|
|
|
|
|
|
# calculer les options de post-traitement
|
|
|
|
case "$select" in
|
|
|
|
none|n|"") select=none;;
|
|
|
|
inner-join|inner|join|j) select=inner-join;;
|
|
|
|
left-join|left|l) select=left-join;;
|
|
|
|
right-join|right|r) select=right-join;;
|
|
|
|
left-only|lo) select=left-only;;
|
|
|
|
right-only|ro) select=right-only;;
|
|
|
|
*)
|
|
|
|
ewarn "$select: valeur de --select invalide. Elle sera ignorée"
|
|
|
|
select=none
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
if [ "$postproc" == "auto" ]; then
|
|
|
|
case "$select" in
|
|
|
|
left-only) lkeepf="*"; rkeepf=""; postproc=1;;
|
|
|
|
right-only) lkeepf=""; rkeepf="*"; postproc=1;;
|
|
|
|
*) lkeepf="*"; rkeepf="*"; postproc=;;
|
|
|
|
esac
|
|
|
|
fi
|
|
|
|
if [ -n "$postproc" ]; then
|
|
|
|
if [ "$lkeepf" == "--NOT-SET--" ]; then
|
|
|
|
case "$select" in
|
|
|
|
right-only) lkeepf=;;
|
|
|
|
*) lkeepf="*";;
|
|
|
|
esac
|
|
|
|
fi
|
|
|
|
if [ "$rkeepf" == "--NOT-SET--" ]; then
|
|
|
|
case "$select" in
|
|
|
|
left-only) rkeepf=;;
|
|
|
|
*) rkeepf="*";;
|
|
|
|
esac
|
|
|
|
fi
|
|
|
|
local -a tmpfields fields
|
|
|
|
local field src dest
|
|
|
|
if [ -n "$lcopyf" ]; then
|
|
|
|
array_split tmpfields "$lcopyf" ,
|
|
|
|
fields=()
|
|
|
|
for field in "${tmpfields[@]}"; do
|
|
|
|
splitpair "$field" dest src
|
|
|
|
[ -n "$src" ] || src="$dest"
|
|
|
|
array_add fields "$dest:$src"
|
|
|
|
done
|
|
|
|
lcopyf="$(array_join fields ,)"
|
|
|
|
fi
|
|
|
|
if [ -n "$rcopyf" ]; then
|
|
|
|
array_split tmpfields "$rcopyf" ,
|
|
|
|
fields=()
|
|
|
|
for field in "${tmpfields[@]}"; do
|
|
|
|
splitpair "$field" dest src
|
|
|
|
[ -n "$src" ] || src="$dest"
|
|
|
|
array_add fields "$dest:$src"
|
|
|
|
done
|
|
|
|
rcopyf="$(array_join fields ,)"
|
|
|
|
fi
|
|
|
|
if [ "$lskipf" == "*" ]; then
|
|
|
|
lskipf=
|
|
|
|
lkeepf=
|
|
|
|
fi
|
|
|
|
if [ -n "$lskipf" ]; then
|
|
|
|
[ "$lkeepf" == "*" ] && lkeepf="$lheaders"
|
|
|
|
array_split fields "$lkeepf" ,
|
|
|
|
array_split tmpfields "$lskipf" ,
|
|
|
|
for field in "${tmpfields[@]}"; do
|
|
|
|
array_del fields "$field"
|
|
|
|
done
|
|
|
|
lkeepf="$(array_join fields ,)"
|
|
|
|
fi
|
|
|
|
if [ "$rskipf" == "*" ]; then
|
|
|
|
rskipf=
|
|
|
|
rkeepf=
|
|
|
|
fi
|
|
|
|
if [ -n "$rskipf" ]; then
|
|
|
|
[ "$rkeepf" == "*" ] && rkeepf="$rheaders"
|
|
|
|
array_split fields "$rkeepf" ,
|
|
|
|
array_split tmpfields "$rskipf" ,
|
|
|
|
for field in "${tmpfields[@]}"; do
|
|
|
|
array_del fields "$field"
|
|
|
|
done
|
|
|
|
rkeepf="$(array_join fields ,)"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
local -a lcopyfs rcopyfs lkeepfs rkeepfs
|
|
|
|
array_split lcopyfs "$lcopyf" ,
|
|
|
|
array_split rcopyfs "$rcopyf" ,
|
|
|
|
array_split lkeepfs "$lkeepf" ,
|
|
|
|
array_split rkeepfs "$rkeepf" ,
|
|
|
|
|
|
|
|
# fusionner les deux fichiers
|
|
|
|
local tmpmerged
|
|
|
|
ac_set_tmpfile tmpmerged "" __mergecsv_merged "" __MERGECSV_DEBUG
|
|
|
|
csort -s -k 1,$(($padlen + 1)) "$tmpleft" "$tmpright" >"$tmpmerged"
|
|
|
|
|
2017-08-24 12:15:16 +04:00
|
|
|
<"$tmpmerged" lawkrun -f \
|
2013-08-27 15:14:44 +04:00
|
|
|
padlen:int="$padlen" \
|
|
|
|
parse_headers:int="$parse_headers" ignore_case:int="$ignore_case" \
|
|
|
|
lheaderscsv="$lheaders" lkey="$lkey" lprefix="$lprefix" \
|
|
|
|
rheaderscsv="$rheaders" rkey="$rkey" rprefix="$rprefix" \
|
|
|
|
select="$select" postproc:int="$postproc" \
|
|
|
|
lcopyfs[@] rcopyfs[@] \
|
|
|
|
lkeepfs[@] rkeepfs[@] \
|
|
|
|
"$__AWKCSV_FUNCTIONS"'
|
|
|
|
function lgeth(field, nbfields, i) {
|
2014-09-30 09:41:28 +04:00
|
|
|
nbfields = array_len(lheaders)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (int(field) == field) {
|
|
|
|
field = int(field)
|
|
|
|
if (field >= 1 && field <= nbfields) return field
|
|
|
|
else return 0
|
|
|
|
}
|
|
|
|
field = tolower(field)
|
|
|
|
for (i = 1; i <= nbfields; i++) {
|
|
|
|
if (field == tolower(lheaders[i])) {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function rgeth(field, nbfields, i) {
|
2014-09-30 09:41:28 +04:00
|
|
|
nbfields = array_len(rheaders)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (int(field) == field) {
|
|
|
|
field = int(field)
|
|
|
|
if (field >= 1 && field <= nbfields) return field
|
|
|
|
else return 0
|
|
|
|
}
|
|
|
|
field = tolower(field)
|
|
|
|
for (i = 1; i <= nbfields; i++) {
|
|
|
|
if (field == tolower(rheaders[i])) {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function copyf(lfields, rfields, i, fs, vs, l, r) {
|
|
|
|
for (i = 1; i <= lcopyfs_count; i++) {
|
|
|
|
fs = lcopyfs[i]
|
|
|
|
match(fs, /(.*):(.*)/, vs)
|
|
|
|
l = vs[1]; l = lgeth(l)
|
|
|
|
r = vs[2]; r = rgeth(r)
|
|
|
|
if (l && r) {
|
|
|
|
lfields[l] = rfields[r]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 1; i <= rcopyfs_count; i++) {
|
|
|
|
fs = rcopyfs[i]
|
|
|
|
match(fs, /(.*):(.*)/, vs)
|
|
|
|
l = vs[2]; l = lgeth(l)
|
|
|
|
r = vs[1]; r = rgeth(r)
|
|
|
|
if (l && r) {
|
|
|
|
rfields[r] = lfields[l]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function keepf(lfields, rfields, i) {
|
|
|
|
for (i = lskipfs_count; i >= 1; i--) {
|
|
|
|
array_deli(lfields, lskipfs[i])
|
|
|
|
}
|
|
|
|
for (i = rskipfs_count; i >= 1; i--) {
|
|
|
|
array_deli(rfields, rskipfs[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function printmerged(lline, rline, nocopy, linecsv, tmplinecsv) {
|
2014-09-30 09:41:28 +04:00
|
|
|
if (lline != "") array_parsecsv(lfields, lline, array_len(lheaders))
|
|
|
|
else array_newsize(lfields, array_len(lheaders))
|
|
|
|
if (rline != "") array_parsecsv(rfields, rline, array_len(rheaders))
|
|
|
|
else array_newsize(rfields, array_len(rheaders))
|
2013-08-27 15:14:44 +04:00
|
|
|
if (postproc) {
|
|
|
|
if (!nocopy) copyf(lfields, rfields)
|
|
|
|
keepf(lfields, rfields)
|
|
|
|
}
|
|
|
|
linecsv = array_formatcsv(lfields)
|
|
|
|
tmplinecsv = array_formatcsv(rfields)
|
2016-02-10 11:45:24 +04:00
|
|
|
if (array_len(rfields) > 0) {
|
|
|
|
if (array_len(lfields) > 0) linecsv = linecsv ","
|
2013-08-27 15:14:44 +04:00
|
|
|
linecsv = linecsv tmplinecsv
|
|
|
|
}
|
|
|
|
print linecsv
|
|
|
|
}
|
|
|
|
BEGIN {
|
|
|
|
if (parse_headers) {
|
|
|
|
array_parsecsv(lheaders, lheaderscsv)
|
2014-09-30 09:41:28 +04:00
|
|
|
lheaders_count = array_len(lheaders)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (lprefix != "") {
|
|
|
|
for (i = 1; i <= lheaders_count; i++) {
|
|
|
|
lheaders[i] = lprefix lheaders[i]
|
|
|
|
}
|
|
|
|
lheaderscsv = array_formatcsv(lheaders)
|
|
|
|
}
|
|
|
|
lkey = lgeth(lkey)
|
|
|
|
if (!lkey) lkey = 1
|
|
|
|
|
|
|
|
array_parsecsv(rheaders, rheaderscsv)
|
2014-09-30 09:41:28 +04:00
|
|
|
rheaders_count = array_len(rheaders)
|
2013-08-27 15:14:44 +04:00
|
|
|
if (rprefix != "") {
|
|
|
|
for (i = 1; i <= rheaders_count; i++) {
|
|
|
|
rheaders[i] = rprefix rheaders[i]
|
|
|
|
}
|
|
|
|
rheaderscsv = array_formatcsv(rheaders)
|
|
|
|
}
|
|
|
|
rkey = rgeth(rkey)
|
|
|
|
if (!rkey) rkey = 1
|
|
|
|
}
|
|
|
|
LEFT = 1
|
|
|
|
RIGHT = 2
|
|
|
|
hasleft = 0
|
|
|
|
|
|
|
|
# quelle sélection effectuer?
|
|
|
|
selectjoin = select ~ /none|inner-join|left-join|right-join/
|
|
|
|
selectleft = select ~ /none|left-join|left-only/
|
|
|
|
selectright = select ~ /none|right-join|right-only/
|
|
|
|
|
|
|
|
# liste des indexes de champs a supprimer
|
|
|
|
array_new(lskipfs)
|
|
|
|
if (!in_array("*", lkeepfs)) {
|
|
|
|
for (i = 1; i <= lheaders_count; i++) {
|
|
|
|
field = lheaders[i]
|
|
|
|
if (!in_array(field, lkeepfs)) {
|
|
|
|
fieldi = lgeth(field)
|
|
|
|
if (i != 0) array_add(lskipfs, fieldi)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
asort(lskipfs)
|
|
|
|
}
|
2014-09-30 09:41:28 +04:00
|
|
|
lskipfs_count = array_len(lskipfs)
|
2013-08-27 15:14:44 +04:00
|
|
|
|
|
|
|
array_new(rskipfs)
|
|
|
|
if (!in_array("*", rkeepfs)) {
|
|
|
|
for (i = 1; i <= rheaders_count; i++) {
|
|
|
|
field = rheaders[i]
|
|
|
|
if (!in_array(field, rkeepfs)) {
|
|
|
|
fieldi = rgeth(field)
|
|
|
|
if (i != 0) array_add(rskipfs, fieldi)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
asort(rskipfs)
|
|
|
|
}
|
2014-09-30 09:41:28 +04:00
|
|
|
rskipfs_count = array_len(rskipfs)
|
2015-08-20 07:58:17 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
if (parse_headers) {
|
|
|
|
# afficher les en-têtes après traitement de lkeepfs et rkeepfs, parce que
|
|
|
|
# printmerged() a besoin de lskipfs et rskipfs
|
|
|
|
printmerged(lheaderscsv, rheaderscsv, 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function readleft() {
|
|
|
|
lprefix = substr($0, 1, padlen)
|
|
|
|
lwhich = substr($0, padlen + 1, 1)
|
|
|
|
if (lwhich == "1") lwhich = LEFT; else lwhich = RIGHT
|
|
|
|
lline = substr($0, padlen + 2)
|
|
|
|
hasleft = 1
|
|
|
|
}
|
|
|
|
function readright() {
|
|
|
|
rprefix = substr($0, 1, padlen)
|
|
|
|
rwhich = substr($0, padlen + 1, 1)
|
|
|
|
if (rwhich == "1") rwhich = LEFT; else rwhich = RIGHT
|
|
|
|
rline = substr($0, padlen + 2)
|
|
|
|
}
|
|
|
|
function right2left() {
|
|
|
|
lprefix = rprefix
|
|
|
|
lwhich = rwhich
|
|
|
|
lline = rline
|
|
|
|
hasleft = 1
|
|
|
|
}
|
|
|
|
!hasleft {
|
|
|
|
readleft()
|
|
|
|
next
|
|
|
|
}
|
|
|
|
{
|
|
|
|
readright()
|
|
|
|
if (lprefix == rprefix && lwhich == LEFT && rwhich == RIGHT) {
|
|
|
|
if (selectjoin) printmerged(lline, rline)
|
|
|
|
hasleft = 0
|
|
|
|
next
|
|
|
|
} else {
|
|
|
|
if (lwhich == LEFT && selectleft) {
|
|
|
|
printmerged(lline, "")
|
|
|
|
} else if (lwhich == RIGHT && selectright) {
|
|
|
|
printmerged("", lline)
|
|
|
|
}
|
|
|
|
right2left()
|
|
|
|
next
|
|
|
|
}
|
|
|
|
}
|
|
|
|
END {
|
|
|
|
if (hasleft) {
|
|
|
|
if (lwhich == LEFT && selectleft) {
|
|
|
|
printmerged(lline, "")
|
|
|
|
} else if (lwhich == RIGHT && selectright) {
|
|
|
|
printmerged("", lline)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
'
|
2014-01-21 11:41:41 +04:00
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
ac_clean "$tmpleft" "$tmpright" "$tmpmerged"
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
function cmergecsv() { LANG=C lmergecsv "$@"; }
|
|
|
|
function mergecsv() { LANG=C lmergecsv "$@"; }
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
|
|
|
|
__SORTCSV_HELP="\
|
|
|
|
Trier un fichier csv sur la valeur d'un champ
|
|
|
|
|
2016-02-29 23:27:21 +04:00
|
|
|
-S, --skip-lines nblines
|
2013-08-27 15:14:44 +04:00
|
|
|
Sauter nblines au début du flux
|
2016-02-29 23:27:21 +04:00
|
|
|
-h, -H, --parse-headers
|
2013-08-27 15:14:44 +04:00
|
|
|
Lire la liste des champs à partir de la première ligne non ignorée des flux.
|
|
|
|
Si cette option est spécifiée (ce qui est le cas par défaut), le champ
|
|
|
|
spécifié avec l'option -k peut être le nom effectif du champ. Sinon, le
|
|
|
|
champ ne peut être que numérique.
|
2016-02-29 23:27:21 +04:00
|
|
|
-N, --numkeys
|
2013-08-27 15:14:44 +04:00
|
|
|
Ne pas analyser la première ligne pour les noms des champs. Les champs
|
|
|
|
spécifiés ne peuvent être que numériques.
|
|
|
|
-k, --key FIELD
|
|
|
|
Spécifier le champ utilisé pour le tri. Si --parse-headers n'est pas
|
|
|
|
spécifié, cette valeur doit être numérique. La valeur par défaut est 1.
|
|
|
|
--no-headers
|
|
|
|
Ne pas afficher les en-têtes en sortie.
|
|
|
|
|
|
|
|
-n, --numeric-sort
|
|
|
|
Comparer selon la valeur numérique du champ
|
|
|
|
-i, -f, --ignore-case
|
|
|
|
Comparer sans tenir compte de la casse
|
|
|
|
-r, --reverse
|
|
|
|
Inverser l'ordre de tri
|
|
|
|
-s, --stable
|
|
|
|
Stabiliser le tri en inhibant la comparaison de dernier recours
|
|
|
|
-u, --unique
|
|
|
|
Ne garder que la première occurence de plusieurs entrées identiques
|
|
|
|
rencontrées. Note: la correspondance se fait sur toute l'entrée, pas
|
|
|
|
uniquement sur la valeur de la clé.
|
|
|
|
-o, --output OUTPUT
|
|
|
|
Ecrire le résultat dans OUTPUT au lieu de la sortie standard"
|
|
|
|
|
|
|
|
: "${__SORTCSV_DEBUG:=}"
|
|
|
|
function lsortcsv() {
|
|
|
|
# Trier le fichier csv $1. La clé du tri est spécifiée par l'option -k et
|
|
|
|
# vaut 1 par défaut. Les valeurs des clés ne doivent pas faire plus de 64
|
|
|
|
# caractères de long.
|
|
|
|
eval "$(utools_local)"
|
|
|
|
local skip=0 parse_headers=auto key=1 show_headers=1
|
|
|
|
local numeric_sort= ignore_case= reverse_sort= stable_sort= unique_sort= output=
|
|
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
2016-02-29 23:27:21 +04:00
|
|
|
-S:,--skip:,--skip-lines:,--skiplines: skip= \
|
|
|
|
-h,-H,--parse-headers parse_headers=1 \
|
|
|
|
-N,--numkeys parse_headers= \
|
2013-08-27 15:14:44 +04:00
|
|
|
-k:,--key: key= \
|
|
|
|
--no-headers show_headers= \
|
|
|
|
--show-headers show_headers=1 \
|
|
|
|
-n,--numeric-sort numeric_sort=1 \
|
|
|
|
-i,-f,--ignore-case ignore_case=1 \
|
|
|
|
-r,--reverse reverse_sort=1 \
|
|
|
|
-s,--stable stable_sort=1 \
|
|
|
|
-u,--unique unique_sort=1 \
|
|
|
|
-o:,--output: output= \
|
|
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
|
|
|
|
local input="$1"
|
2014-06-10 11:32:34 +04:00
|
|
|
if [ -z "$input" -o "$input" == "-" ]; then
|
2013-08-27 15:14:44 +04:00
|
|
|
input=/dev/stdin
|
|
|
|
elif [ ! -f "$input" ]; then
|
|
|
|
eerror "$input: fichier introuvable"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
local padding="----------------------------------------------------------------"
|
|
|
|
local padlen=${#padding}
|
|
|
|
local headers
|
|
|
|
|
2014-06-10 11:32:34 +04:00
|
|
|
local -a tmpfiles
|
|
|
|
|
2013-08-27 15:14:44 +04:00
|
|
|
[ "$parse_headers" == "auto" ] && parse_headers=1
|
2014-06-10 11:32:34 +04:00
|
|
|
if [ -n "$parse_headers" -a -z "$headers" ]; then
|
|
|
|
if [ "$input" == /dev/stdin ]; then
|
|
|
|
# Si on lit depuis stdin, il faut faire une copie du flux dans un
|
|
|
|
# fichier temporaire pour calculer les en-têtes
|
|
|
|
local tmpinput
|
|
|
|
ac_set_tmpfile tmpinput "" __sortcsv_input0 "" __SORTCSV_DEBUG
|
|
|
|
array_add tmpfiles "$tmpinput"
|
|
|
|
cat >"$tmpinput"
|
|
|
|
input="$tmpinput"
|
|
|
|
fi
|
2017-08-24 12:15:16 +04:00
|
|
|
headers="$(<"$input" lawkrun skip:int="$skip" 'NR <= skip { next } { print; exit }')"
|
2013-08-27 15:14:44 +04:00
|
|
|
fi
|
|
|
|
|
|
|
|
# faire le fichier de travail
|
|
|
|
local tmpinput
|
|
|
|
ac_set_tmpfile tmpinput "" __sortcsv_input "" __SORTCSV_DEBUG
|
2014-06-10 11:32:34 +04:00
|
|
|
array_add tmpfiles "$tmpinput"
|
2017-08-24 12:15:16 +04:00
|
|
|
<"$input" >"$tmpinput" lawkrun -f \
|
2013-08-27 15:14:44 +04:00
|
|
|
padding="$padding" padlen:int="$padlen" \
|
|
|
|
skip:int="$skip" parse_headers:int="$parse_headers" \
|
|
|
|
key="$key" \
|
|
|
|
"$__AWKCSV_FUNCTIONS"'
|
|
|
|
NR <= skip { next }
|
|
|
|
parse_headers && do_once("parse-headers") {
|
|
|
|
array_parsecsv(HEADERS, $0)
|
|
|
|
key = geth(key)
|
|
|
|
if (!key) key = 1
|
|
|
|
next
|
|
|
|
}
|
|
|
|
{
|
|
|
|
oline = $0
|
|
|
|
parsecsv($0)
|
|
|
|
keyvalue = substr($key, 1, padlen)
|
|
|
|
print keyvalue substr(padding, 1, padlen - length(keyvalue)) oline
|
|
|
|
}
|
|
|
|
'
|
|
|
|
|
|
|
|
# trier le fichier de travail
|
|
|
|
local tmpsorted
|
|
|
|
ac_set_tmpfile tmpsorted "" __sortcsv_sorted "" __SORTCSV_DEBUG
|
2014-06-10 11:32:34 +04:00
|
|
|
array_add tmpfiles "$tmpsorted"
|
2013-08-27 15:14:44 +04:00
|
|
|
args=(# arguments de sort
|
|
|
|
${numeric_sort:+-n} ${ignore_case:+-f}
|
|
|
|
${reverse_sort:+-r} ${stable_sort:+-s}
|
|
|
|
${unique_sort:+-u}
|
|
|
|
)
|
|
|
|
csort -k 1,$(($padlen + 1)) "${args[@]}" <"$tmpinput" >"$tmpsorted"
|
|
|
|
|
|
|
|
# résultat
|
|
|
|
[ -n "$output" ] || output=/dev/stdout
|
2017-08-24 12:15:16 +04:00
|
|
|
stdredir "$tmpsorted" "$output" "" lawkrun -f \
|
2013-08-27 15:14:44 +04:00
|
|
|
padlen:int="$padlen" \
|
|
|
|
headerscsv="$headers" show_headers:int="$show_headers" \
|
|
|
|
'
|
|
|
|
BEGIN { if (show_headers) print headerscsv }
|
|
|
|
{ print substr($0, padlen + 1) }
|
|
|
|
'
|
|
|
|
|
2014-06-10 11:32:34 +04:00
|
|
|
ac_clean "${tmpfiles[@]}"
|
2013-08-27 15:14:44 +04:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
function csortcsv() { LANG=C lsortcsv "$@"; }
|
|
|
|
function sortcsv() { LANG=C lsortcsv "$@"; }
|
2016-02-29 22:01:49 +04:00
|
|
|
|
|
|
|
################################################################################
|
|
|
|
|
|
|
|
__DUMPCSV_HELP="\
|
2017-04-02 19:09:51 +04:00
|
|
|
Afficher les champs spécifiés pour traitement par le shell ou par awk
|
2016-02-29 22:01:49 +04:00
|
|
|
|
2016-03-01 00:28:27 +04:00
|
|
|
-S, --skip-lines nblines
|
2016-02-29 22:01:49 +04:00
|
|
|
Sauter nblines au début du flux
|
2016-03-01 00:28:27 +04:00
|
|
|
-H, --parse-headers
|
2016-02-29 22:01:49 +04:00
|
|
|
Lire la liste des champs à partir de la première ligne non ignorée du flux.
|
|
|
|
Si cette option est spécifiée (ce qui est le cas par défaut), les champs
|
|
|
|
spécifiés peuvent être les noms effectifs des champs. Sinon, les champs ne
|
|
|
|
peuvent être que numériques.
|
2016-02-29 23:16:35 +04:00
|
|
|
-N, --numkeys
|
2016-02-29 22:01:49 +04:00
|
|
|
Ne pas analyser la première ligne pour les noms des champs. Les champs
|
|
|
|
spécifiés ne peuvent être que numériques.
|
|
|
|
|
2016-03-01 00:28:27 +04:00
|
|
|
-k, --keep-fields KEEPFIELDS
|
|
|
|
Garder les champs spécifiés. Les autres sont supprimés de la sortie.
|
|
|
|
KEEPFIELDS est une liste de champs séparés par des virgules.
|
|
|
|
-s, --skip-fields SKIPFIELDS
|
|
|
|
Exclure les champs spécifiés. SKIPFIELDS est une liste de champs séparés par
|
|
|
|
des virgules.
|
2016-03-01 07:05:51 +04:00
|
|
|
|
2016-03-01 00:28:27 +04:00
|
|
|
-h, --dump-headers
|
|
|
|
Inclure les en-têtes dans la sortie. Elles sont traitées exactement comme
|
|
|
|
une ligne de données.
|
2016-03-01 07:05:51 +04:00
|
|
|
--hname HNAME
|
|
|
|
Spécifier le nom à utiliser pour afficher les en-têtes avec l'option -h
|
2016-03-01 00:28:27 +04:00
|
|
|
|
2016-02-29 22:01:49 +04:00
|
|
|
-n, --name NAME
|
2017-04-02 19:09:51 +04:00
|
|
|
Spécifier le nom à utiliser pour les options -f, -a, -b, -w
|
2016-02-29 22:01:49 +04:00
|
|
|
-f, --function
|
|
|
|
Afficher les champs comme l'appel d'une fonction, e.g:
|
|
|
|
dump value00 value01...
|
|
|
|
dump value10 value11...
|
|
|
|
C'est la méthode d'affichage par défaut. L'option -n permet de spécifier le
|
2016-03-01 07:05:51 +04:00
|
|
|
nom de la fonction, qui vaut 'dump' par défaut. Avec -h, le nom par défaut
|
|
|
|
est 'dumph'
|
2016-02-29 22:01:49 +04:00
|
|
|
-a, --array
|
|
|
|
Afficher les champs comme les valeurs d'un tableau, e.g:
|
2016-06-09 17:32:55 +04:00
|
|
|
values=()
|
2016-02-29 22:01:49 +04:00
|
|
|
values=(value00 value01...)
|
|
|
|
values=(value10 value11...)
|
2016-06-10 05:37:45 +04:00
|
|
|
Bien entendu, cela n'a sens que si une seule ligne résultat est attendue.
|
2016-02-29 23:16:35 +04:00
|
|
|
L'option -n permet de spécifier le nom du tableau, qui vaut 'values' par
|
2016-03-01 07:05:51 +04:00
|
|
|
défaut. Avec -h, le nom par défaut est 'names'
|
2016-06-09 17:32:55 +04:00
|
|
|
Cette méthode commence par afficher de quoi initialiser le tableau à une
|
|
|
|
valeur vide. Ainsi, si aucune ligne n'est trouvée, le tableau est néanmoins
|
|
|
|
initialisé. Si ce comportement n'est pas désiré, il est possible d'utiliser
|
2016-06-10 05:37:45 +04:00
|
|
|
l'option --no-reset
|
2016-02-29 23:16:35 +04:00
|
|
|
-b, --array-function
|
|
|
|
Afficher les champs comme l'initialisation des valeurs d'un tableau suivi de
|
|
|
|
l'appel d'une fonction, e.g:
|
|
|
|
values=(value00 value01...)
|
|
|
|
dump
|
|
|
|
values=(value10 value11...)
|
|
|
|
dump
|
|
|
|
Le nom du tableau est fixé à 'values'. L'option -n permet de spécifier le
|
2016-03-01 07:05:51 +04:00
|
|
|
nom de la fonction, qui vaut 'dump' par défaut. Avec -h le nom par défaut
|
2016-06-10 05:37:45 +04:00
|
|
|
est 'dumph'
|
2017-04-02 19:09:51 +04:00
|
|
|
-w, --awk-map
|
|
|
|
Afficher les champs comme une fonction awk qui permet de rechercher une
|
|
|
|
valeur et d'afficher la valeur correspondante. L'option -n permet de
|
|
|
|
spécifier le nom de la fonction, qui vaut 'mapval' par défaut.
|
|
|
|
--wtype TYPE
|
|
|
|
Spécifier le type de fonction à générer avec l'option --awk-map. Le type par
|
|
|
|
défaut est 'value'
|
|
|
|
Avec le type 'value', la fonction générée est de la forme dump(inval). Cette
|
|
|
|
fonction prend en argument la valeur de la colonne spécifiée avec --wscol,
|
|
|
|
et si cette valeur est trouvée dans les données, retourner la valeur de la
|
|
|
|
colonne spécifiée avec --wrcol
|
|
|
|
Avec le type 'array', la fonction générée est de la forme dump(inval, outvs)
|
|
|
|
Cette fonction prend en argument la valeur de la colonne spécifiée avec
|
|
|
|
--wscol, et si cette valeur est trouvée dans les données, le tableau outvs
|
|
|
|
est rempli avec les donnée de la ligne correspondante.
|
|
|
|
--wscol SFIELD
|
|
|
|
Nom du champ qui est cherché par la fonction générée avec l'option --awk-map
|
|
|
|
La valeur par défaut est '1', c'est à dire le premier champ.
|
|
|
|
--wrcol RFIELD
|
|
|
|
Nom du champ dont la valeur est retournée par la fonction générée avec
|
|
|
|
l'option '--awk-map --wtype value' en cas de correspondance.
|
|
|
|
La valeur par défaut est '2', c'est à dire le deuxième champ.
|
2016-06-10 05:37:45 +04:00
|
|
|
-v, --var
|
|
|
|
Affiche les champ en initialisant des variables, e.g:
|
|
|
|
names=(header0 header1)
|
|
|
|
header0=
|
|
|
|
header1=
|
|
|
|
header0=value00
|
|
|
|
header1=value01
|
|
|
|
...
|
|
|
|
header0=value10
|
|
|
|
header1=value11
|
|
|
|
...
|
|
|
|
Bien entendu, cela n'a sens que si une seule ligne résultat est attendue.
|
|
|
|
Cette option implique -h. Le nom par défaut du tableau qui liste les
|
|
|
|
variables est names.
|
|
|
|
Cette méthode commence par réinitialiser les variables à la valeur vide.
|
|
|
|
Ainsi, si aucune ligne n'est trouvée, les variables sont vides. Si ce
|
|
|
|
comportement n'est pas désiré, il est possible d'utiliser l'option
|
|
|
|
--no-reset"
|
2016-02-29 22:01:49 +04:00
|
|
|
|
|
|
|
function ldumpcsv() {
|
|
|
|
eval "$(utools_local)"
|
2016-03-01 00:28:27 +04:00
|
|
|
local skip= parse_headers=1 keepf skipf show_headers
|
2016-06-10 05:37:45 +04:00
|
|
|
local dump=function reset=1 name hname
|
2017-04-02 19:09:51 +04:00
|
|
|
local wtype wscol wrcol
|
2016-02-29 22:01:49 +04:00
|
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
2016-03-01 00:28:27 +04:00
|
|
|
-S:,--skip:,--skip-lines:,--skiplines: skip= \
|
|
|
|
-H,--parse-headers parse_headers=1 \
|
2016-02-29 23:16:35 +04:00
|
|
|
-N,--numkeys parse_headers= \
|
2016-03-01 00:28:27 +04:00
|
|
|
-k:,--keep:,--keep-fields:,--keepfields: keepf= \
|
|
|
|
-s:,--skip:,--skip-fields:,--skipfields: skipf= \
|
|
|
|
-h,--dump-headers show_headers=1 \
|
2016-02-29 22:01:49 +04:00
|
|
|
-n:,--name: name= \
|
2016-03-01 07:05:51 +04:00
|
|
|
--hname: hname= \
|
2016-02-29 22:01:49 +04:00
|
|
|
-f,--function dump=function \
|
|
|
|
-a,--array dump=array \
|
2016-06-10 05:37:45 +04:00
|
|
|
--reset reset=1 \
|
|
|
|
--no-reset reset= \
|
2016-02-29 23:16:35 +04:00
|
|
|
-b,--array-function dump=array-function \
|
2017-04-02 19:09:51 +04:00
|
|
|
-w,--awk-map dump=awk-map \
|
|
|
|
--wtype: wtype= \
|
|
|
|
--wscol: wscol= \
|
|
|
|
--wrcol: wrcol= \
|
2016-06-10 05:37:45 +04:00
|
|
|
-v,--var dump=var \
|
2016-02-29 22:01:49 +04:00
|
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
|
2016-03-01 00:28:27 +04:00
|
|
|
args=(
|
|
|
|
${skip:+--skip-lines "$skip"}
|
|
|
|
${keepf:+--keep-fields "$keepf"}
|
|
|
|
${skipf:+--skip-fields "$skipf"}
|
|
|
|
)
|
2016-02-29 22:01:49 +04:00
|
|
|
if [ -n "$parse_headers" ]; then array_add args -h
|
|
|
|
else array_add args -n
|
|
|
|
fi
|
|
|
|
|
2016-06-10 05:37:45 +04:00
|
|
|
[ "$dump" == var ] && show_headers=1
|
|
|
|
|
2016-03-01 07:05:51 +04:00
|
|
|
if [ -z "$hname" ]; then
|
|
|
|
case "$dump" in
|
|
|
|
function|array-function) hname=dumph;;
|
2016-06-10 05:37:45 +04:00
|
|
|
array|var) hname=names;;
|
2016-03-01 07:05:51 +04:00
|
|
|
esac
|
|
|
|
fi
|
2016-02-29 22:01:49 +04:00
|
|
|
if [ -z "$name" ]; then
|
|
|
|
case "$dump" in
|
2016-02-29 23:16:35 +04:00
|
|
|
function|array-function) name=dump;;
|
2016-02-29 22:01:49 +04:00
|
|
|
array) name=values;;
|
2017-04-02 19:09:51 +04:00
|
|
|
awk-map) name=mapval;;
|
2016-02-29 22:01:49 +04:00
|
|
|
esac
|
|
|
|
fi
|
2017-04-02 19:09:51 +04:00
|
|
|
case "${wtype:-value}" in
|
|
|
|
array|a) wtype=array;;
|
|
|
|
value|v) wtype=value;;
|
|
|
|
esac
|
|
|
|
[ -n "$wscol" ] || wscol=1
|
|
|
|
[ -n "$wrcol" ] || wrcol=2
|
2016-02-29 22:01:49 +04:00
|
|
|
|
|
|
|
local -a fields
|
|
|
|
fields=("$@")
|
|
|
|
|
2017-04-02 19:09:51 +04:00
|
|
|
awkcsv "${args[@]}" -v show_headers:int="$show_headers" \
|
|
|
|
-v hname="$hname" -v name="$name" -v dump="$dump" \
|
|
|
|
-v reset_values:int="$reset" -v fields[@] \
|
|
|
|
-v wtype="$wtype" -v wscol="$wscol" -v wrcol="$wrcol" \
|
|
|
|
-e '
|
|
|
|
function init_fields() {
|
|
|
|
if (do_once("init_fields")) {
|
|
|
|
if (fields_count == 0) {
|
|
|
|
array_copy(fields, HEADERS)
|
|
|
|
fields_count = array_len(fields)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function before_awkmap(name, wtype) {
|
|
|
|
if (do_once("before_awkmap")) {
|
|
|
|
if (wtype == "value") {
|
|
|
|
print "function " name "(inval) {"
|
|
|
|
} else if (wtype == "array") {
|
|
|
|
print "function " name "(inval, outvs) {"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function awkmap(inval, outval, values, count, wtype, i) {
|
|
|
|
if (wtype == "value") {
|
|
|
|
print "if (inval == " qawk(inval) ") return " qawk(outval)
|
|
|
|
} else if (wtype == "array") {
|
|
|
|
print "if (inval == " qawk(inval) ") {"
|
|
|
|
print "delete outvs"
|
|
|
|
for (i = 1; i <= count; i++) {
|
|
|
|
print "outvs[" i "] = " qawk(values[i])
|
|
|
|
}
|
|
|
|
print "return 1"
|
|
|
|
print "}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function after_awkmap() {
|
|
|
|
if (do_once("after_awkmap")) {
|
|
|
|
if (wtype == "value") {
|
|
|
|
print "return \"\""
|
|
|
|
} else if (wtype == "array") {
|
|
|
|
print "return 0"
|
|
|
|
}
|
|
|
|
print "}"
|
|
|
|
}
|
|
|
|
}
|
2016-06-10 05:37:45 +04:00
|
|
|
function dump_values(name, values, dump) {
|
|
|
|
if (dump == "function") {
|
|
|
|
print qarr(values, name)
|
|
|
|
} else if (dump == "array") {
|
|
|
|
print name "=(" qarr(values) ")"
|
|
|
|
} else if (dump == "array-function") {
|
|
|
|
print "values=(" qarr(values) ")"
|
|
|
|
print name
|
|
|
|
} else if (dump == "var") {
|
|
|
|
i = 1
|
|
|
|
while (i <= fields_count) {
|
|
|
|
print fields[i] "=" qval(values[i])
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-02 19:09:51 +04:00
|
|
|
function before_dump() {
|
|
|
|
if (do_once("before_dump")) {
|
2016-06-09 17:32:55 +04:00
|
|
|
if (show_headers) {
|
2016-06-10 05:37:45 +04:00
|
|
|
dump_values(hname, fields, dump == "var"? "array": dump)
|
2016-06-09 17:32:55 +04:00
|
|
|
}
|
2016-06-10 05:37:45 +04:00
|
|
|
if ((dump == "array" || dump == "var") && reset_values) {
|
|
|
|
array_new(reset)
|
|
|
|
dump_values(name, reset, dump)
|
2016-06-09 17:32:55 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-01 00:28:27 +04:00
|
|
|
{
|
2016-06-09 17:32:55 +04:00
|
|
|
init_fields()
|
2016-03-01 00:28:27 +04:00
|
|
|
array_new(values)
|
|
|
|
i = 1
|
|
|
|
while (i <= fields_count) {
|
|
|
|
values[i] = get(fields[i])
|
|
|
|
i++
|
|
|
|
}
|
2017-04-02 19:09:51 +04:00
|
|
|
|
|
|
|
if (dump == "awk-map") {
|
|
|
|
before_awkmap(name, wtype)
|
|
|
|
if (wscol ~ /^[0-9]+$/) inval = geti(wscol)
|
|
|
|
else inval = get(wscol)
|
|
|
|
if (wrcol ~ /^[0-9]+$/) outval = geti(wrcol)
|
|
|
|
else outval = get(wrcol)
|
|
|
|
awkmap(inval, outval, values, fields_count, wtype)
|
|
|
|
} else {
|
|
|
|
before_dump()
|
|
|
|
dump_values(name, values, dump)
|
|
|
|
}
|
2016-06-09 17:32:55 +04:00
|
|
|
}
|
|
|
|
END {
|
2017-04-02 19:09:51 +04:00
|
|
|
if (dump == "awk-map") {
|
|
|
|
before_awkmap(name, wtype)
|
|
|
|
after_awkmap()
|
|
|
|
} else {
|
|
|
|
before_dump()
|
|
|
|
}
|
2016-03-01 00:28:27 +04:00
|
|
|
}' -a ''
|
2016-02-29 22:01:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function cdumpcsv() { LANG=C ldumpcsv "$@"; }
|
|
|
|
function dumpcsv() { LANG=C ldumpcsv "$@"; }
|
2016-02-29 23:55:54 +04:00
|
|
|
|
|
|
|
################################################################################
|
|
|
|
|
|
|
|
__PRINTCSV_HELP="\
|
|
|
|
Afficher les valeurs spécifiées au format CSV
|
|
|
|
|
|
|
|
-F, --fields FIELDS
|
|
|
|
Spécifier les champs en sortie
|
|
|
|
-o, --output OUTPUT
|
|
|
|
Ajouter la ligne au fichier spécifié. Si le fichier n'existe pas ou est de
|
|
|
|
taille vide, et que l'option -F est spécifiée, alors écrire les en-têtes
|
|
|
|
dans le fichier avant d'écrire les valeurs.
|
|
|
|
--show-headers
|
|
|
|
-n, --no-headers
|
|
|
|
Forcer l'affichage (resp. le non-affichage) des en-têtes"
|
|
|
|
|
|
|
|
function lprintcsv() {
|
|
|
|
eval "$(utools_local)"
|
|
|
|
local fields output show_headers=auto
|
|
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
|
|
|
-F:,--fields: fields= \
|
|
|
|
-o:,--output: output= \
|
|
|
|
-n,--no-headers show_headers= \
|
|
|
|
--show-headers show_headers=1 \
|
|
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
|
|
|
|
array_split fields "$fields" ,
|
|
|
|
[ "$output" == - ] && output=
|
|
|
|
[ -n "$fields" ] || show_headers=
|
|
|
|
if [ "$show_headers" == auto ]; then
|
|
|
|
if [ -n "$output" ]; then
|
|
|
|
[ -s "$output" ] || show_headers=1
|
|
|
|
else
|
|
|
|
show_headers=
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
[ -n "$output" ] || output=/dev/stdout
|
|
|
|
values=("$@")
|
|
|
|
|
2017-08-24 12:15:16 +04:00
|
|
|
stdredir "" ">>$output" "" lawkrun -f fields[@] show_headers:int="$show_headers" values[@] '
|
2016-02-29 23:55:54 +04:00
|
|
|
BEGIN {
|
|
|
|
if (show_headers) array_printcsv(fields)
|
|
|
|
if (fields_count > 0) count = fields_count
|
|
|
|
else count = values_count
|
|
|
|
array_new(output)
|
|
|
|
for (i = 1; i <= count; i++) {
|
|
|
|
if (i <= values_count) output[i] = values[i]
|
|
|
|
else output[i] = ""
|
|
|
|
}
|
|
|
|
array_printcsv(output)
|
|
|
|
}'
|
|
|
|
}
|
|
|
|
|
|
|
|
function cprintcsv() { LANG=C lprintcsv "$@"; }
|
|
|
|
function printcsv() { LANG=C lprintcsv "$@"; }
|