#!/bin/bash
#-------------------------------------------------------------------------------------------
#<Beschreibung>
#  Filtert Spalten in CSV-Dateien oder blendet Spalten aus und addiert optional Spaltensummen
#</Beschreibung>
#-------------------------------------------------------------------------------------------

. /usr/local/bins/common

function callAwk() { #{{{{
  awk -F ";" -v enumerate="$enumerate" -v filter="$filter" -v headers="$headers" -v exclude="$exclude" -v calculate="$calculate" -v codad=$commaOrDotAsDecimal '
    @include "/usr/local/bins/commonroutines.awk"
    BEGIN{
      if(codad == 1){
        commaOrDotAsDecimal=1;
      }
      else if(codad == 0){
        commaOrDotAsDecimal=0;
      }
      else{
        # andernfalls greift Voreinstellung in commonroutines.awk
      }
      #-------------------------------------------------------------------------------------------------
      OFS=";";
      #-------------------------------------------------------------------------------------------------
      # Spalten fuer Addition ermitteln
      #-------------------------------------------------------------------------------------------------
      numCalculations=split(calculate,dummyArr,":");
      for(i=1;i<=numCalculations;i++){
        col=dummyArr[i];
        calculateHash[col]="j";
        printf("Spalte: %3.3s => Addition\n",col) >> "/dev/stderr" ;
      }
      #-------------------------------------------------------------------------------------------------
      # Ausblendungen ermitteln
      #-------------------------------------------------------------------------------------------------
      numExcludes=split(exclude,dummyArr,":");
      for(i=1;i<=numExcludes;i++){
        col=dummyArr[i];
        excludeHash[col]="j";
        printf("Spalte: %3.3s => Ausschluss\n",col) >> "/dev/stderr" ;
      }
      #-------------------------------------------------------------------------------------------------
      # Beizubehaltende (Header-)Zeilen ermitteln
      #-------------------------------------------------------------------------------------------------
      numHeaders=split(headers,dummyArr,":");
      for(i=1;i<=numHeaders;i++){
        row=dummyArr[i];
        headerHash[row]="j";
        printf("Zeile: %3.3s => Beibehalt\n",row) >> "/dev/stderr" ;
      }
      #-------------------------------------------------------------------------------------------------
      # Spalten-Filter-Kombinationen trennen
      #   1:!empty#3:Hugo => 
      #     1:!empty
      #     3:Hugo
      #-------------------------------------------------------------------------------------------------
      numFilters=split(filter,filterArr,"#");
      #-------------------------------------------------------------------------------------------------
      # Nun Spalten und ihre Filter ermitteln
      #   1:!empty =>
      #     1
      #     !empty
      #-------------------------------------------------------------------------------------------------
      for(i=1;i<=numFilters;i++){
        dummy=filterArr[i];                  # 1:!empty
        numElements=split(dummy,dummyArr,":");
        if(numElements != 2){
          die("Konnte Spaltenfilter nicht in 2 Teile zerlegen: " dummy) > /dev/stderr ;
        }
        col=dummyArr[1];
        colFil=dummyArr[2];
        #-----------------------------------------------------------------------------------------------
        # Liegt ein negativer Filter vor ?
        #   !empty => empty und Eintrag in Negativregister
        #-----------------------------------------------------------------------------------------------
        if(substr(colFil,1,1) == "!"){
          colFil=substr(colFil,2);  # !empty => empty
          negHash[col]="j";
        }
        #-----------------------------------------------------------------------------------------------
        # Liegt ein numerischer Vergleichsfilter vor ? (<,>,<=,>=)
        #   <= 0.5 wird zu 0.5 und Eintrag in Numerikregister
        #-----------------------------------------------------------------------------------------------
        else if(substr(colFil,1,1) ~ /[<>]/){
          #---------------------------------------------------------------------------------------------
          # <=, >=
          #---------------------------------------------------------------------------------------------
          if(substr(colFil,2,1) == "="){
            numHash[col]=substr(colFil,1,2);  # >=
            colFil=substr(colFil,3);          # >=5 wird zu 5
          }
          #---------------------------------------------------------------------------------------------
          # <, >
          #---------------------------------------------------------------------------------------------
          else{
            numHash[col]=substr(colFil,1,1);  # >
            colFil=substr(colFil,2);          # >5 wird zu 5
          }
        }
        #------------------------------------------------------------------------------------------------
        # noch kein Filter fuer diese Spalte ? => aufnehmen
        #------------------------------------------------------------------------------------------------
        if(col in colFilHash){
          die("Spalte " col " wurde mehrfach angegeben") > /dev/stderr ;
        }
        colFilHash[col]=colFil;     
      }
      #-------------------------------------------------------------------------------------------------
      # Debug
      #-------------------------------------------------------------------------------------------------
      for(col in colFilHash){
        if(col in negHash){
          kindOfFilter="negative";
        }
        else if(col in numHash){
          kindOfFilter="numeric";
        }
        else{
          kindOfFilter="positive";
        }
        printf("Spalte: %3.3s => Filter: |%s| (%s)\n",col,colFilHash[col],kindOfFilter) >> "/dev/stderr" ;
      }
      #-------------------------------------------------------------------------------------------------
    }
    #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    {
      #-------------------------------------------------------------------------------------------------
      # Filter anwenden, sofern keine beizubehaltende Headerzeile
      #-------------------------------------------------------------------------------------------------
      if(! (NR in headerHash)){
        for(col in colFilHash){
          if(int(col) > NF){
            die("Feldzahl " NF " ueberschritten (" col ")") > /dev/stderr ;
          }
          value=$(col);
          #-----------------------------------------------------------------------------------------------
          # Hat Spalte einen Filter ?
          #-----------------------------------------------------------------------------------------------
          if(col in colFilHash){
            filter=colFilHash[col];
            #---------------------------------------------------------------------------------------------
            # Ist der Filter numerisch ? (<=,>=,<,>)
            #---------------------------------------------------------------------------------------------
            if(col in numHash){
              number=$col;
              #-------------------------------------------------------------------------------------------
              # Spalte keine Zahl ? => Zeile ausfiltern
              #-------------------------------------------------------------------------------------------
              if(! isNumber(number)){
                printf("Spalte %3.3s Zeile %5.5s: Keine Zahl: %s\n",col,FNR,number) > "/dev/stderr" ;
                next;
              }
              number=convertNumber(number);
              if(numHash[col] == "<"){
                if(number >= (filter+0)){    # +0 => Zwangskonvertierung zu Zahl
                  next;
                }
              }
              else if(numHash[col] == "<="){
                if(number > (filter+0)){    # +0 => Zwangskonvertierung zu Zahl
                  next;
                }
              }
              else if(numHash[col] == ">"){
                if(number <= (filter+0)){    # +0 => Zwangskonvertierung zu Zahl
                  next;
                }
              }
              else if(numHash[col] == ">="){
                if(number < (filter+0)){    # +0 => Zwangskonvertierung zu Zahl
                  next;
                }
              }
            }
            #---------------------------------------------------------------------------------------------
            # Ist der Filter negativ ?
            #---------------------------------------------------------------------------------------------
            else if(col in negHash){
              if(filter == "empty"){
                if((! value) || (value ~ /^[0,.]+$/)){     # Kein Wert oder irgendeine 0-Darstellung [0,.]
                  next;
                }
              }
              else{
                #-----------------------------------------------------------------------------------------
                # Ist Filter ein Pattern ?
                #-----------------------------------------------------------------------------------------
                if(filter ~ /[].*+|\\]/){
                  # printf("Korrekt erkannter Filter: |%s|\n",filter) >> "/dev/stderr" ;
                  if(value ~ filter){
                    next;
                  }
                }
                #-----------------------------------------------------------------------------------------
                # Nein => vollstaendiger Vergleich (String oder Zahl)
                #-----------------------------------------------------------------------------------------
                else{
                  if(value == filter){
                    next;
                  }
                }
              }
            }
            #---------------------------------------------------------------------------------------------
            # Positiv-Filter
            #---------------------------------------------------------------------------------------------
            else{
              if(filter == "empty"){
                if(value && (! (value ~ /^[0,.]+$/))){      # Wert und keine Art der 0-Darstellung [0,.]
                  next;
                }
              }
              else{
                #-----------------------------------------------------------------------------------------
                # Ist Filter ein Pattern ?
                #-----------------------------------------------------------------------------------------
                if(filter ~ /[].*+|\\]/){
                  # printf("Korrekt erkannter Filter: |%s|\n",filter) >> "/dev/stderr" ;
                  if(! (value ~ filter)){
                    next;
                  }
                }
                #-----------------------------------------------------------------------------------------
                # Nein => vollstaendiger Vergleich (String oder Zahl)
                #-----------------------------------------------------------------------------------------
                else{
                  if(value != filter){
                    next;
                  }
                }
              }
            }
          }
        }
      }
      #----------------------------------------------------------------------------------------------------------------------------------
      # Kein Filter traf zu => ggf. Summation ausgewaehlter Spalten (sofern nicht ausgeblendet) und Ausgabe nicht auszublendender Spalten
      #----------------------------------------------------------------------------------------------------------------------------------
      for(i=1;i<=NF;i++){
        #------------------------------------------------------------------------------------
        # Spalte auszublenden ?
        #------------------------------------------------------------------------------------
        if(i is in excludeHash){
          $i="";
        }
        #------------------------------------------------------------------------------------
        # Spaltenwert addieren ?
        #------------------------------------------------------------------------------------
        else if(i in calculateHash){
          number=trim($i); 
          if(isNumber(number)){
            sum[i]+=convertNumber(number);
          }
          else{
            printf("Spalte %3.3s Zeile %5.5s: Keine Zahl: %s\n",i,FNR,number) > "/dev/stderr" ;
          }
        }
      }
      #-------------------------------------------------------------------------------------------------
      # Zeilennummer genau dann der Ausgabe voranstellen, wenn dieser Lauf einer
      # von mehreren ist und die Gesamtausgabe mehrerer Laeufe anschließend "unique" zu sortieren ist
      #-------------------------------------------------------------------------------------------------
      if(enumerate == "j"){
        printf("%05d#",NR);    # => 00001# als Zeilenanfang (geeignet fuer spaetere numerische Sortierung)
      }
      print;   # <= Das ist die einzige Nutzausgabe (der durch OFS getrennten Felder :-)
    }
    #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    END{
      printf("%150.150s\n",hashLine) > "/dev/stderr" ;
      for(col in sum){
        printf("Summe Spalte %3.3s: %15.1f\n",col,sum[col]) > "/dev/stderr" ;
      }
      printf("%150.150s\n",hashLine) > "/dev/stderr" ;
    }
  ' "$infile" >> $outfile
  checkReturn "awk-Fehler"
}
#}}}}
function getRegexFromFile() { #{{{{
  regex=""
  #---------------------------------------------------------------------------------------
  # Eingabe: <col>:[!]<file>
  #   3:[!]/home/heinz/Teams
  # Ausgabe: Regulaerer Ausdruck der Kriterein aus <file>
  #  [!](a|b|c..)
  # Eingabe durch globale Variable tokenFile
  # Ausgabe in globale Variable regex 
  #---------------------------------------------------------------------------------------
  local token=""
  local neg=""
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Ausdruck der Art 3:!/home/heinz/Teams ? ('!' ist optional)
  # Filterausdruck der Art 3:(a|b|c...) oder 3:!(a|b|c) erstellen ...
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  [[ ! "$tokenFile" =~ ^[0-9]+:[!]{0,1} ]] && die "Ungueltiger Parameter fuer -I/i: $tokenFile (<col>:[!]<file>)"
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Spalte extrahieren
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  col=${tokenFile%%:*}            # 3:/home/heinz/Teams => 3
  [[ "$col" =~ ^.*[^0-9].*$ ]] && die "Konnte Spalte nicht extrahieren: $tokenFile"
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Negativer Filter ?
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  case "$tokenFile" in
    *!*) neg="!" ;;
      *) neg=""  ;;
  esac
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Eigentliche Datei extrahieren
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  tokenFile=${tokenFile#*:}        # 3:!/home/heinz/Teams => !/home/heinz/Teams
  tokenFile=${tokenFile#!}         #   !/home/heinz/Teams =>  /home/heinz/Teams
  verifyFile "$tokenFile"
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Token zu regulaerem Ausdruck verknuepfen
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  while read token; do
    [[ "$token" =~ ^[[:blank:]]*# ]] && continue   # Kommentarzeilen ueberspringen
    token=${token%%+([[:blank:]])$}                # Trailing Whitespace beschneiden (shopt -s extglob)
    regex="${regex:+${regex}|}${token}"            # a|b|c...
  done < "$tokenFile" 
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  # Nur eine oder mehrere Auspraegungen ? => in Filter-Array uebernehmen
  #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  if [[ "$regex" =~ \| ]]; then               # Trennsymbol, d.h. mehr als eine Auspraegung ?
    regex="${col}:${neg}^(${regex})$"         # 3:^(a|b|c...)$
  else                                        # kein Trennsymbol => nur _eine_ Auspraegung
    regex="${col}:${neg}^${regex}$"           # 3:^hugo$
  fi 
}
#}}}}
function usage(){ #{{{{
  cat <<EOF
    
     Cave: Eingabedatei wird konvertiert erwartet (ASCII|UTF-8, Unix, keine Umlaute ...)

           Ohne Eingabedatei wird von stdin gelesen

           Neben stdout wird auch die Datei $outfile erzeugt.

     #########################################################################################################################################

     $0 [-f(ilter) <col>:[!]<filter>[#<col>:[!]<filter>[..]] 
        [-F <filtername> [ -F .. ]] 
        [-i <col>:[!]<file1>[#<col>:[!]<file2>[..]] 
        [-I <col>:[!]<pfad zu auspraegungsdatei> [ -I .. ]]
        [-e(xclude) <excludes>] 
        [-c(calculate) <cols>] 
        [-d(otOrCommaAsDecimal <(0|1)>] 
        [-H(eaderrows) <row>[:<row>...]] 
        [<infile>]

     #########################################################################################################################################

     Cave: Filteroptionen (-f,-F,-I) koennen beliebig oft angegeben und kombiniert werden.
           Innerhalb eines jeden Filters darf jede Spalte nur EINMAL vorkommen.
           Die _Kriterien_ eines jeden Filters werden und-verknuepft.
           Die _Filteroptionen_ selbst jedoch werden oder-verknuepft.
           
           Beispiel

           $0 -f "3:Projekt.*" -f "4:Team.*" -F default -I 4:/home/heinz/teams  <infile>

           Cave: Bei multiplen -f Optionen entfaellt Option -c

     -f: <col>:[!]<filter>[#<col>:[!]<filter>[..]] 

         und-verknuepfte Filterung aller Zeilen nach Strings, Zahlen oder regulareren Ausdruecken


         Verkettung von Spalten und Filtern
         Trennug der Spalten-Filter durch "#"
         Trennung von Spalte und ihrem Filter durch ":"
         negative Filter mit Prefix "!"
         numerische Vergleiche mit Prefixen
           - <,>,<=,>=
             - Cave: Ist der Spaltenwert keine Zahl, wird die zugehoerige Zeile ausgefiltert
             - Cave: Dezimaltrenner fuer Vergleichsoperanden ist '.', also
                 $0 -f '8:>4.5' Hugo.csv

         Cave: "0" entspricht numerisch 0, wird also von jeder 0-Darstellung (0.00, ...) erfuellt

         Cave: "empty" entspricht false in awk oder einem beliebigen 0-Wert (also [0,.])

         Cave: Enthaelt der Filter "typische" Regex-Sonderzeichen (cf. Code), wird er als Pattern interpretiert
            Rapp|Koch
            .*Koch
              Cave: * ist nicht Shell-Wildcard, sondern Quantifier, also nicht
                *
              sondern
                .*
            \.prod
            Sales\\sTask
            Sales[ \\t]sTask
              Cave: Ein '\' muss 2-mal angegeben werden, da das Pattern als dynamic regexp zunaechst als String verarbeitet wird

            Achtung: Folgende Filterungen sind synonym 
                     mycsvfilter -f '7:Intern#6:ja#21:A-CT1CSI[35].*'  Teamschnitt_generiert.csv 
                     mycsvfilter -f '7:Intern#6:ja#21:A-CT1CSI(3|5).*' Teamschnitt_generiert.csv

     -F: <filtername> <Datei>
         
         convenience-Funktion: Verwenden eines in Datei $filterConfig beschriebenen Filters im Format
           <filternam>;<filter>

           Beispiel

             budget;2:!^400(0088|0001|0117|0273|0296|0336|0390|0520|0532|0597|0598|0615|0618|0923|1001|1017|1026|1027)

             Cave: ';' darf damit im Filter selbst neben '#', '!' und ':' als Sonderzeichen nicht vorkommen

     -i: <col>:[!]<file1>[#<col>:[!]<file2>[..]] 

         und-Verknuefpung der Filterauspraegungen der jeweiligen Datei 
           cf. -f und -I

     -I: <col>:[!]<auspraegungsdatei>

         Filterung anhand _einer_ Spalte ueber die Auspraegungen aus einer Datei.
           => hieraus lassen sich Oder-Verknuepfungen von Spalten mit vielen Auspraegungen realisieren.
         Ein optionales '!' negiert die Liste.

         Die Auspraegungsdatei enthaelt alle Werte, welche spalte <col> annehmen darf
           Diese Werte duerfen auch regulaere Ausdruecke sein, z.B.
             .* Document Serv.*

           mycsvfilter -I 5:/home/heinz/Teams -I 6:/home/heinz/Scopes ...
             oder-verknuepfte Filterung der Spalten 5 und 6 nach Auspraegungen von Mustern in angegebenen Dateien

         Erzeugt wird ein Filterausdruck der Art
           (a|b|.* Doc Serv.*)
           !(a|b|[0-9].*)

     -H: Beibehaltung der Zeilen mit den angegebenen Nummern

         Trennung der Zeilen durch :
      
           -H 1:2:3

     -c: Summation von Spalten

         Cave: Option unzulaessig bei multiplen Filtern (mehreren oder-verknuepften -f Optionen)
         
         Trennung der Spalten durch :
           
           -c 2:6:8

         Cave: Summation NACH Filterung

     -d: Interpretation von .|, vor 3 Ziffern als Dezimaltrenner oder Tausendertrennzeichen (Default: Wert aus commonroutines.awk)
         
         0 oder 1

     -e: Ausblenden von Spalten
         
         Trennung der Spalten durch :
           
         Cave: -e 2:3:4  => Ausblenden der Spalten
               -e '!2:3:4' => Anzeige genau nur dieser Spalten (Ausblenden aller anderen - negativer Filter mit vorangestelltem '!')
         Cave: Explizite Einschluesse sind auf 100 Spalten begrenzt

     Beispiel

     $0 -e 1:14:15 -f 6:match#1:!Sales#3:empty#4:!empty#5:0#7:!0#8:!(Breinlinger[^,]*,[[:space:]]*Heinz) Hugo.csv
        
        Ausblenden
          Spalten 1, 14 und 15 
        Filtern
          Spalte 6: "match"
          Spalte 1: Nicht "Sales"
          Spalte 3: leer (== kein Wert, also Leer-String)
          Spalte 4: nicht leer
          Spalte 5: 0
          Spalte 7: nicht 0
          Spalte 8: nicht Pattern

     $0 -f 4:<=0.8 Hugo.csv

EOF
} #}}}}
#{{{{ variables
calculate=""
commaOrDotAsDecimal=""
convenienceFilter=""
enumerate=n
exclude=""
filter=""
filterArr=()
filterConfig=filter.config
headers=""
infile=""
outfile=filtered.csv
regex=""
dummy=""
tmp=""
tmpArr=()
#}}}}
#{{{{ getOpts
while getopts "c:d:he:f:F:H:I:i:" opt; do
  case $opt in
    c) calculate=$OPTARG 
       if [[ "$calculate" =~ [^0-9:] ]]; then
         die "Ungueltige Spaltenliste: $calculate" > /dev/stderr
       fi ;;
    d) commaOrDotAsDecimal=$OPTARG 
       case $commaOrDotAsDecimal in
         0|1) ;;
           *) die "Ungueltiger Wert fuer Dezimal-/Tausendertrennvorgabe: $commaOrDotAsDecimal" > /dev/stderr ;;
       esac ;;
    h) usage; exit ;;
    H) headers=$OPTARG 
       if [[ "$headers" =~ [^0-9:] ]]; then
         die "Ungueltige Spaltenliste: $headers" > /dev/stderr
       fi ;;
    e) exclude=$OPTARG 
       if [[ "$exclude" =~ [^0-9:!] ]]; then
         die "Ungueltige Spaltenliste: $exclude" > /dev/stderr
       fi 
       #-------------------------------------------------------------------------------------------
       # Negativer Auschluss, also expliziter Einschluss ? 
       #   => Konvertieren
       #   !2:3:4  => 1:5:6:7:....:100
       #-------------------------------------------------------------------------------------------
       if [[ "$exclude" =~ ^! ]]; then
         dummy=:${exclude:1}:                  # '!' beschneiden und Begrenzer setzen: 2:3:4 => :2:3:4:
         exclude=""
         for i in {1..100}; do
           if [[ ! "$dummy" =~ :${i}: ]]; then # falls :2:3:4: nicht matched auf :3:
             exclude=${exclude:+${exclude}:}$i # Verkettung: 1:5:6:.....
           fi
         done
       fi ;;
    f) #-------------------------------------------------------------------------------------------
       # Filteroption darf mehrfach vorkommen
       #-------------------------------------------------------------------------------------------
       filterArr+=( "$OPTARG" ) ;;  
    F) #-------------------------------------------------------------------------------------------
       # Filter mit Namensbezeichner in Konfigurationsdatei suchen
       # Diese Option darf mehrfach vorkommen
       #-------------------------------------------------------------------------------------------
       verifyFile "$filterConfig"
       convenienceFilter="$OPTARG" 
       [ -z "$convenienceFilter" ] && die "Kein Filtername angegeben"
       found=""
       while IFS=\; read key value; do
         if [ "$key" == $convenienceFilter ]; then
           printf "Filter %s => %s gefunden ...\n" "$key" "$value" > /dev/stderr
           filterArr+=( "$value" )
           found="yes"
           break
         fi
       done < <(sed '/^\s*#/d' $filterConfig)       # Kommentarzeilen ueberspringen
       [ -z "$found" ] && die "Konnte Filter $convenienceFilter nicht finden" > /dev/stderr ;;
    i) #-------------------------------------------------------------------------------------------
       # Filterkriterien aus Datei(en) einlesen.
       #   4:!/home/heinz/Teams
       #   4:!/home/heinz/Teams#5:/home/heinz/Scopes
       #     und-Verknuepfung aller Spalten und derer oder-verknuepfter Auspraegungsfilter
       #        4:!/home/heinz/Teams#5:/home/heinz/Scopes =>  4:!(t1|t2|t3)#5(s1|s2|s3)
       #-------------------------------------------------------------------------------------------
       dummy="$OPTARG"
       #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       # Nur eine Filterspalte ?
       #   4:!/home/heinz/Teams
       #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       if [[ ! "$dummy" =~ \# ]]; then       # escape vor '#'
         tokenFile="$dummy"
         getRegexFromFile 
         filterArr+=( "$regex" )             # 4:!^(t1|t2|t3)$ 
       #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       # mehrere Filterspalten
       #   4:!/home/heinz/Teams#5:/home/heinz/Scopes 
       #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       else
         #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
         # alle durch '#' getrennten Ausdruecke via Here-String in Array einlesen
         #   4:!/home/heinz/Teams#5:/home/heinz/Scopes 
         #   =>
         #     4:!/home/heinz/Teams
         #     5:/home/heinz/Scopes 
         #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
         IFS=\# read -a tmpArr <<< "$dummy"   # escape for '#' hier nicht zwingend
         tmp=""
         for tokenFile in "${tmpArr[@]}"; do
           getRegexFromFile                   # 4:^!(t1|t2|t3)$  und 5:^(s1|s2|s3|s4)$
           tmp=${tmp:+${tmp}#}${regex}        # 4:^!(t1|t2|t3)$#5:^(s1|s2|s3|s4)$
         done 
         filterArr+=( "$tmp" )                # ( ...   4:^!(t1|t2|t3)$#5:^(s1|s2|s3|s4)$ ... )
       fi ;;
    I) #-------------------------------------------------------------------------------------------
       # Filterkriterien aus Datei einlesen.
       # Diese Option darf mehrfach vorkommen
       #   4:!/home/heinz/Teams
       #-------------------------------------------------------------------------------------------
       tokenFile="$OPTARG"
       getRegexFromFile 
       filterArr+=( "$regex" ) ;;       # 4:!^(t1|t2|t3)$ 
    *) die "mycsvfilter: Keine gueltige Option $opt" > /dev/stderr ;;
  esac
done
shift $(( OPTIND -1 ))
infile="$1"
#}}}}
#{{{{ verification
if [ -n "$infile" ]; then
  verifyFile $infile
fi
[ -z "${#filterArr[@]}" -a -z "$convenienceFilter" -a -z "$exclude" ] && die "Kein Filter/Ausschluss angegeben" > /dev/stderr
[ "${#filterArr[@]}" -gt 1 -a -n "$calculate" ] && die "Kalkulation (-c) nicht moeglich bei mehreren Filtern (-f)"
#}}}}
> "$outfile"
#-------------------------------------------------------------------------------------------------------------------------------------------------
# ggf. mehrfacher Aufruf von awk bei mehreren, dann oder-zu-verknuepfenden -f Optionen
# Bei Mehrfachfiltern
# - jeder Ausgabe (angehaengt an dieselbe Ausgabedatei) Zeilennummern voranstellen lassen
# - die kombinierte Ausgabedatei numerisch (auf sich selbst) filtern
# - vorangestellte Zeilennummern loeschen
#-------------------------------------------------------------------------------------------------------------------------------------------------
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Ggf. Zeilennummern voranstellen lassen
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[ "${#filterArr[@]}" -gt 1 ] && enumerate="j"   # Bei Mehrfachfiltern der Ausgabe Zeilennummern voranstellen lassen
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Ggf. mutipel filtern
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for filter in "${filterArr[@]}"; do
  callAwk
done
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Ggf. Sortierung (numerisch, unique) auf sich selbst und
# Loeschen der vorangestellten Zeilennummern ( 00001# .... )
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if [ "${#filterArr[@]}" -gt 1 ]; then           
  sort -n -u -o "$outfile" "$outfile"
  checkReturn "sort-Fehler"
  sed -i 's/^[0-9]\+#//' "$outfile"
  checkReturn "sed-Fehler"
fi
#-------------------------------------------------------------------------------------------------------------------------------------------------
# Falls keinerlei Filter angegeben => Aufruf von awk mit restlichen Parametern, z.B. Spaltenausblendung
#-------------------------------------------------------------------------------------------------------------------------------------------------
if [ "${#filterArr[@]}" -eq 0 ]; then           
  callAwk
fi
#-------------------------------------------------------------------------------------------------------------------------------------------------
# Ausgabe der Ergebnisdatei auf die Standardausgabe
#-------------------------------------------------------------------------------------------------------------------------------------------------
cat "$outfile"
