allow to have AUTOYEAR instead of e.g. 2011
[u/mrichter/AliRoot.git] / PWGPP / scripts / utilities.sh
1 #!/usr/bin/env bash
2 #library of useful PWGPP related bash functions
3 #it REQUIRES BASH 4 !!!!
4 #blame: Mikolaj Krzewicki, mkrzewic@cern.ch
5
6 if [ ${BASH_VERSINFO} -lt 4 ]; then
7   echo "bash version >= 4 needed, you have ${BASH_VERSION}, exiting..."
8   exit 1
9 fi
10
11 PWGPP_runMap="
12 2010 108350 139517
13 2011 140441 170593
14 2012 171590 193766
15 2013 194308 199146
16 2014 202369 206695
17 2015 999999 999999
18 2016 999999 999999
19 "
20
21 parseConfig()
22 {
23   #parse command line arguments, they have to be in the form
24   #  option=value
25   #they are then set in the environment
26   #
27   #optionally a config file can be specified in the arguments:
28   #  configFile=<someFile>
29   #config file sets variables: option=value
30   #command line options override config file
31   #
32   #recommended way of using (at the beginning of your funcion/script):
33   #  if ! parseConfig "${@}"; then return; fi
34   
35   local args=("$@")
36   local opt=""
37   
38   #first check if we will need to decode spaces
39   local encodedSpaces=""
40   for opt in "${args[@]}"; do
41     [[ "${opt}" =~ encodedSpaces=.* ]] \
42       && encodedSpaces=1 \
43       && break
44   done
45
46   #then look for a configFile (if any)
47   for opt in "${args[@]}"; do
48     if [[ ${opt} =~ configFile=.* ]]; then
49       eval "${opt}"
50       [[ ! -f ${configFile} ]] \
51         && echo "configFile ${configFile} not found, exiting..." \
52         && return 1
53       echo "using config file: ${configFile}"
54       source "${configFile}"
55       break
56     fi
57   done
58
59   #then, parse the options as they override the options from configFile
60   for opt in "${args[@]}"; do
61     [[ -n ${encodedSpaces} ]] && opt="$(decSpaces ${opt})"
62     if [[ ! "${opt}" =~ .*=.* ]]; then
63       echo "badly formatted option ${var}, should be: option=value, stopping..."
64       return 1
65     fi
66     local var="${opt%%=*}"
67     local value="${opt#*=}"
68     #echo "${var}=${value}"
69     export ${var}="${value}"
70   done
71   return 0
72 }
73
74 guessRunData()
75 {
76   #guess the period from the path, pick the rightmost one
77   #if $ocdbStorage is set it will be reset to the anchorYear (for MC)
78   period=""
79   runNumber=""
80   year=""
81   pass=""
82   legoTrainRunNumber=""
83   dataType=""
84   originalPass=""
85   originalPeriod=""
86   anchorYear=""
87   shortRunNumber=""
88
89   local oldIFS=${IFS}
90   local IFS="/"
91   [[ -z ${1} ]] && return 1
92   declare -a path=( $1 )
93   IFS="${oldIFS}"
94   local dirDepth=$(( ${#path[*]}-1 ))
95   for ((x=${dirDepth};x>=0;x--)); do
96
97     [[ $((x-1)) -ge 0 ]] && local fieldPrev=${path[$((x-1))]}
98     local field=${path[${x}]}
99     local fieldNext=${path[$((x+1))]}
100
101     [[ ${field} =~ ^[0-9]*$ && ${fieldNext} =~ (.*\.zip$|.*\.root$) ]] && legoTrainRunNumber=${field}
102     [[ -n ${legoTrainRunNumber} && -z ${pass} ]] && pass=${fieldPrev}
103     [[ ${field} =~ ^LHC[0-9][0-9][a-z].*$ ]] && period=${field%_*} && originalPeriod=${field}
104     [[ ${field} =~ ^000[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && runNumber=${field#000}
105     [[ ${field} =~ ^[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && shortRunNumber=${field}
106     [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
107     [[ ${field} =~ ^(^sim$|^data$) ]] && dataType=${field}
108   done
109   originalPass=${pass}
110
111   if [[ ${dataType} =~ sim ]]; then
112     [[ -n ${shortRunNumber} && -z ${runNumber} ]] && runNumber=${shortRunNumber}
113     pass="passMC"
114     originalPass="" #for MC not from lego, the runNumber is identified as lego train number, thus needs to be nulled
115     anchorYear=$(run2year $runNumber)
116     if [[ -z "${anchorYear}" ]]; then
117       echo "WARNING: anchorYear not available for this production: ${originalPeriod}, runNumber: ${runNumber}. Cannot set the OCDB."
118       return 1
119     fi
120     #modify the OCDB: set the year
121     ocdbStorage=$(setYear ${anchorYear} ${ocdbStorage})
122   else
123     ocdbStorage=$(setYear ${year} ${ocdbStorage})
124   fi
125
126   [[ -n ${shortRunNumber} && -z ${runNumber} && -z {dataType} ]] && runNumber=${shortRunNumber}
127   [[ -n ${shortRunNumber} && "${legoTrainRunNumber}" =~ ${shortRunNumber} ]] && legoTrainRunNumber=""
128   [[ -z ${legoTrainRunNumber} && ${dataType} == "data" ]] && pass=${path[$((dirDepth-1))]}
129   [[ -n ${legoTrainRunNumber} ]] && pass+="_lego${legoTrainRunNumber}"
130   
131   #if [[ -z ${dataType} || -z ${year} || -z ${period} || -z ${runNumber}} || -z ${pass} ]];
132   if [[ -z ${runNumber} ]]
133   then
134     #error condition
135     return 1
136   fi
137   
138   #ALL OK
139   return 0
140 }
141
142 guessRunNumber()
143 (
144   #guess the run number from the path, pick the rightmost one
145   if guessRunData "${1}"; then
146     echo ${runNumber}
147     return 0
148   fi
149   return 1
150 )
151
152 guessYear()
153 (
154   #guess the year from the path, pick the rightmost one
155   if guessRunData "${1}"; then
156     echo ${year}
157     return 0
158   fi
159   return 1
160 )
161
162 guessPeriod()
163 (
164   #guess the period from the path, pick the rightmost one
165   if guessRunData "${1}"; then
166     echo ${period}
167     return 0
168   fi
169   return 1
170 )
171
172 setYear()
173 {
174   #set the year in the string
175   #usualy used to modify the year in $ocdbStorage
176   #  ${1} - year to be set
177   #  ${2} - where to set the year
178   #if AUTOYEAR is present in target - it will be replaced by the year
179   local yearSource=$(guessYearFast ${1})
180   local yearTarget=$(guessYearFast ${2})
181   local path=${2}
182   [[ ${yearSource} -ne ${yearTarget} && -n ${yearTarget} && -n ${yearSource} ]] \
183     && path=${2/\/"${yearTarget}"\//\/"${yearSource}"\/}
184   echo ${path}
185   return 0
186 }
187
188 guessYearFast()
189 {
190   #guess the year from the path, pick the rightmost one
191   #is string AUTOYEAR present, will be returned
192   local IFS="/"
193   declare -a pathArray=( ${1} )
194   local field=""
195   local year=""
196   local autoYear=""
197   for field in ${pathArray[@]}; do
198     [[ ${field} =~ ^20[0-9][0-9]$ ]] && year="${field}"
199     [[ ${field} == AUTOYEAR ]] && autoYear="${field}"
200   done
201   [[ -n ${autoYear} ]] && year="${autoYear}"
202   echo ${year}
203   return 0
204 }
205
206 run2year()
207 {
208   #for a given run print the year.
209   #the run-year table is ${PWGPP_runMap} (a string)
210   #one line per year, format: year runMin runMax
211   local run=$1
212   [[ -z ${run} ]] && return 1
213   local year=""
214   local runMin=""
215   local runMax=""
216   while read year runMin runMax; do
217     [[ -z ${year} || -z ${runMin} || -z ${runMax} ]] && continue
218     [[ ${run} -ge ${runMin} && ${run} -le ${runMax} ]] && echo ${year} && break
219   done < <(echo "${PWGPP_runMap}")
220   return 0
221 }
222
223 hostInfo(){
224 #
225 # Hallo world -  Print AliRoot/Root/Alien system info
226 #
227
228 #
229 # HOST info
230 #
231     echo --------------------------------------
232         echo 
233         echo HOSTINFO
234         echo 
235         echo HOSTINFO HOSTNAME"      "$HOSTNAME
236         echo HOSTINFO DATE"          "`date`
237         echo HOSTINFO gccpath"       "`which gcc` 
238         echo HOSTINFO gcc version"   "`gcc --version | grep gcc`
239         echo --------------------------------------    
240
241 #
242 # ROOT info
243 #
244         echo --------------------------------------
245         echo
246         echo ROOTINFO
247         echo 
248         echo ROOTINFO ROOT"           "`which root`
249         echo ROOTINFO VERSION"        "`root-config --version`
250         echo 
251         echo --------------------------------------
252
253
254 #
255 # ALIROOT info
256 #
257         echo --------------------------------------
258         echo
259         echo ALIROOTINFO
260         echo 
261         echo ALIROOTINFO ALIROOT"        "`which aliroot`
262         echo ALIROOTINFO VERSION"        "`echo $ALICE_LEVEL`
263         echo ALIROOTINFO TARGET"         "`echo $ALICE_TARGET`
264         echo 
265         echo --------------------------------------
266
267 #
268 # Alien info
269 #
270 #echo --------------------------------------
271 #echo
272 #echo ALIENINFO
273 #for a in `alien --printenv`; do echo ALIENINFO $a; done 
274 #echo
275 #echo --------------------------------------
276
277 #
278 # Local Info
279 #
280         echo PWD `pwd`
281         echo Dir 
282         ls -al
283         echo
284         echo
285         echo
286   
287   return 0
288 }
289
290 summarizeLogs()
291 {
292   #validate and summarize the status of logs
293   #input is a list of logs, or a glob:
294   #example (summarizes logs in current and subdirs):
295   #  summarizeLogs * */*
296   #if no args given, process all files in PWD
297   #exit code 1 if some logs are not validated
298
299   #print a summary of logs
300   local input
301   local file=""
302   declare -A files
303   input=("${@}")
304   [[ -z "${input[*]}" ]] && input=( "${PWD}"/* )
305   
306   #double inclusion protection+make full paths
307   for file in "${input[@]}"; do
308     [[ ! "${file}" =~ ^/ ]] && file="${PWD}/${file}"
309     files["${file}"]="${file}"
310   done
311
312   local logFiles
313   logFiles="\.*log$|^stdout$|^stderr$"
314
315   #check logs
316   local logStatus=0
317   local errorSummary=""
318   local validationStatus=""
319   declare -A coreFiles
320   for file in "${files[@]}"; do
321     [[ ! -f ${file} ]] && continue
322     #keep track of core files for later processing
323     [[ "${file##*/}" =~ ^core$ ]] && coreFiles[${file}]="${file}" && continue
324     [[ ! "${file##*/}" =~ ${logFiles} ]] && continue
325     errorSummary=$(validateLog ${file})
326     validationStatus=$?
327     [[ validationStatus -ne 0 ]] && logStatus=1
328     if [[ ${validationStatus} -eq 0 ]]; then 
329       #in pretend mode randomly report an error in rec.log some cases
330       echo "${file} OK"
331     elif [[ ${validationStatus} -eq 1 ]]; then
332       echo "${file} BAD ${errorSummary}"
333     elif [[ ${validationStatus} -eq 2 ]]; then
334       echo "${file} OK MWAH ${errorSummary}"
335     fi
336   done
337
338   #report core files
339   for x in "${coreFiles[@]}"; do
340     echo ${x}
341     chmod 644 ${x}
342     #gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
343     gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace.log
344     local nLines[2]
345     #nLines=($(wc -l stacktrace_${x//\//_}.log))
346     nLines=($(wc -l stacktrace.log))
347     if [[ ${nLines[0]} -eq 0 ]]; then
348       #rm stacktrace_${x//\//_}.log
349       rm stacktrace.log
350     else
351       logStatus=1
352       echo "${x%/*}/stacktrace.log"
353     fi
354   done
355
356   return ${logStatus}
357 }
358
359 validateLog()
360 {
361   #validate one log file
362   #input is path to log file
363   #output an error summary on stdout
364   #exit code is 0 if validated, 1 otherwise
365   log=${1}
366   errorConditions=(
367             'There was a crash'
368             'floating'
369             'error while loading shared libraries'
370             'std::bad_alloc'
371             's_err_syswatch_'
372             'Thread [0-9]* (Thread'
373             'AliFatal'
374             '\.C.*error:.*\.h: No such file'
375             'segmentation'
376             'Segmentation fault'
377             'Interpreter error recovered'
378             ': command not found'
379             ': comando non trovato'
380             'core dumped'
381   )
382
383   warningConditions=(
384             'This is serious'
385   )
386
387   local logStatus=0
388   local errorSummary=""
389   local warningSummary=""
390   local errorCondition=""
391   for errorCondition in "${errorConditions[@]}"; do
392     local tmp=$(grep -m1 -e "${errorCondition}" ${log})
393     local error=""
394     [[ -n ${tmp} ]] && error=" : ${errorCondition}"
395     errorSummary+=${error}
396   done
397
398   local warningCondition=""
399   for warningCondition in "${warningConditions[@]}"; do
400     local tmp=$(grep -m1 -e "${warningCondition}" ${log})
401     local warning=""
402     [[ -n ${tmp} ]] && warning=" : ${warningCondition}"
403     warningSummary+=${warning}
404   done
405
406   if [[ -n ${errorSummary} ]]; then 
407     echo "${errorSummary}"
408     return 1
409   fi
410
411   if [[ -n ${warningSummary} ]]; then
412     echo "${warningSummary}"
413     return 2
414   fi
415
416   return 0
417 }
418
419 mergeSysLogs()
420 {
421   if [[ $# -lt 2 ]]; then
422     echo 'merge syslogs to an output file'
423     echo 'usage:'
424     echo 'mergeSysLogs outputFile inputFile1 inputFile2 ...'
425     return 0
426   fi
427   local outputFile
428   local inputFiles
429   local i
430   local x
431   local runNumber
432   outputFile=${1}
433   shift
434   inputFiles="$@"
435   i=0
436   if ! ls -1 ${inputFiles} &>/dev/null; then echo "the files dont exist!: ${inputFiles}"; return 1; fi
437   while read x; do 
438     runNumber=$(guessRunNumber ${x})
439     [[ -z ${runNumber} ]] && echo "run number cannot be guessed for ${x}" && continue
440     awk -v run=${runNumber} -v i=${i} 'NR > 1 {print run" "$0} NR==1 && i==0 {print "run/I:"$0}' ${x}
441     (( i++ ))
442   done < <(ls -1 ${inputFiles}) > ${outputFile}
443   return 0
444 }
445
446 stackTraceTree()
447 {
448   if [[ $# -lt 1 ]]; then
449     echo 'make stacktrace processing  in case of standard root crash log'
450     echo 'input is a (list of) text files with the stack trace (either gdb aoutput'
451     echo 'produced with e.g. gdb --batch --quiet -ex "bt" -ex "quit" aliroot core,'
452     echo 'or the root crash log), output is a TTree formatted table.'
453     echo 'example usage:'
454     echo 'benchmark.sh stackTraceTree /foo/*/rec.log'
455     echo 'benchmark.sh stackTraceTree $(cat file.list)'
456     echo 'benchmark.sh stackTraceTree `cat file.list`'
457     return 0
458   fi
459   #cat "${@}" | gawk '
460   gawk '
461        BEGIN { 
462        print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
463                RS="#[0-9]*";
464                aliroot=0;
465                read=1;
466              } 
467       /There was a crash/ {read=1;}
468       /The lines below might hint at the cause of the crash/ {read=0;}
469       read==1 { 
470                if ($3 ~ /Ali*/) aliroot=1; else aliroot=0;
471                gsub("#","",RT); 
472                if ($NF!="" && RT!="" && $3!="") print RT" "$3" "$NF" "0" "aliroot" "FILENAME
473              }
474       ' "${@}" 2>/dev/null
475 }
476
477 plotStackTraceTree()
478 {
479   #plot the stacktrace tree,
480   #first arg    is the text file in the root tree format
481   #second arg   is optional: a plot is written to file instead of screen
482   #third arg    is optional: selection for plotting, default skip G_ stuff
483   local tree=$1
484   local plot=${2:-"crashes.png"}
485   local selection=${3:-'!strstr(method,\"G__\")'}
486   [[ ! -f ${tree} ]] && echo "plotStackTraceTree: no input file given" && return 1
487   aliroot -b <<EOF
488 TTree* t=AliSysInfo::MakeTree("${tree}");
489 TCanvas* canvas = new TCanvas("QA crashes","QA crashes",1);
490 t->Draw("method","${selection}","");
491 canvas->SaveAs("${plot}");
492 .q
493 EOF
494   return 0
495 }
496
497 encSpaces() 
498
499   echo "${1// /±@@±}" 
500 }
501
502 decSpaces() 
503
504   echo "${1//±@@±/ }" 
505 }
506
507 get_realpath() 
508 {
509   if [[ $# -lt 1 ]]; then
510     echo "print the full path of a file or directory, like \"readlink -f\" on linux"
511     echo "Usage:"
512     echo "  get_realpath <someFileOrDir>"
513     return 0
514   fi
515   if [[ -f "$1" ]]
516   then
517     # file *must* exist
518     if cd "$(echo "${1%/*}")" &>/dev/null
519     then
520       # file *may* not be local
521       # exception is ./file.ext
522       # try 'cd .; cd -;' *works!*
523       local tmppwd="$PWD"
524       cd - &>/dev/null
525     else
526       # file *must* be local
527       local tmppwd="$PWD"
528     fi
529   elif [[ -d "$1" ]]; then
530     if cd "$1" &>/dev/null; then
531       local tmppwd="$PWD"
532       cd - &>/dev/null
533       echo "$tmppwd"
534       return 0
535     else
536       return 1
537     fi
538   else
539     # file *cannot* exist
540     return 1 # failure
541   fi
542   # reassemble realpath
543   echo "$tmppwd"/"${1##*/}"
544   return 0 # success
545 }
546
547 printLogStatistics()
548 {
549   #this function processes the summary logs and prints some stats
550   #relies on the summary log format produced by summarizeLogs()
551   # - how many checked logs in total
552   # - number of each type of problem
553   # example usage:
554   #   printLogStatistics */*.log
555   [[ ! -f $1 ]] && return 1
556   echo "log statistics from: ${1%/*}"
557   #cat "${@}" | awk '
558   awk '
559   BEGIN {nOK=0; nCores=0; nStackTraces=0;}
560   /\/core/ {nCores++}
561   /\/stacktrace.log/ {nStackTraces++}
562   /OK/ {nOK++; nLogs++;}
563   /BAD/ {
564     nLogs++
565     err=""
566     write=0
567     for (i=3; i<=NF; i++)
568     { 
569       if ($i ~ /^\:$/) 
570         write=1
571       else
572         write=0
573
574       if (write==0)
575       {
576         if (err=="") err=$i
577         else err=(err FS $i)
578       }
579
580       if (err != "" && (write==1 || i==NF))
581       {
582         sumBAD[err]++
583         err=""
584       }
585     }
586   } 
587   END {
588     print ("number of succesful jobs: " nOK" out of "nLogs )
589     for (key in sumBAD)
590     {
591       print key": "sumBAD[key]
592     }
593     if (nCores>0) print "core files: "nCores", stack traces: "nStackTraces 
594   }
595   ' "${@}"
596 }
597
598 createUniquePID()
599 {
600   #create a unique ID for jobs running in parallel
601   #consists of the ip address of the default network interface, PID,
602   #if an argument is given, append it (e.g. a production ID)
603   #the fields are space separated with a tag for easy parsing
604   #spaces in the productionID will be encoded using encSpaces()
605   local productionID=""
606   [[ -n "${1}" ]] && productionID=$(encSpaces "${1}")
607   local defaultIP=$(/sbin/route | awk '$1=="default" {print $8}' | xargs /sbin/ifconfig | awk '/inet / {print $2}' | sed 's/.*\([0-9]?\.[0-9]?\.[0-9]?\.[0-9]?\)/$1/')
608   local id="ip:${defaultIP} pid:${BASHPID}"
609   [[ -n "${productionID}" ]] && id+=" prod:${productionID}"
610   echo "${id}"
611 }
612
613 copyFileToLocal()
614 (
615   #copies a single file to a local destination: the file may either come from
616   #a local filesystem or from a remote location (whose protocol must be
617   #supported)
618   #copy is "robust" and it is repeated some times in case of failure before
619   #giving up (1 is returned in that case)
620   #origin: Dario Berzano, dario.berzano@cern.ch
621   src="$1"
622   dst="$2"
623   ok=0
624   [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
625
626   proto="${src%%://*}"
627
628   echo "copy file to local dest started: $src -> $dst"
629
630   for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
631
632     echo "...attempt $i of $maxCopyTries"
633     rm -f "$dst"
634
635     if [[ "$proto" == "$src" ]]; then
636       cp "$src" "$dst"
637     else
638       case "$proto" in
639         root)
640           xrdcp -f "$src" "$dst"
641         ;;
642         http)
643           curl -L "$src" -O "$dst"
644         ;;
645         *)
646           echo "protocol not supported: $proto"
647           return 2
648         ;;
649       esac
650     fi
651
652     if [ $? == 0 ] ; then
653       ok=1
654       break
655     fi
656
657   done
658
659   if [[ "$ok" == 1 ]] ; then
660     echo "copy file to local dest OK after $i attempt(s): $src -> $dst"
661     return 0
662   fi
663
664   echo "copy file to local dest FAILED after $maxCopyTries attempt(s): $src -> $dst"
665   return 1
666 )
667
668 paranoidCp()
669 (
670   #recursively copy files and directories
671   #if target is a directory - it must exist!
672   #to avoid using find and the like as they kill
673   #the performance on some cluster file systems
674   #does not copy links to avoid problems
675   sourceFiles=("${@}")
676   destination="${sourceFiles[@]:(-1)}" #last element
677   unset sourceFiles[${#sourceFiles[@]}-1] #remove last element (dst)
678   #[[ ! -f "${destination}" ]] 
679   for src in "${sourceFiles[@]}"; do
680     if [[ -f "${src}" && ! -h  "${src}" ]]; then
681       paranoidCopyFile "${src}" "${destination}"
682     elif [[ -d "${src}" && ! -h "${src}" ]]; then
683       src="${src%/}"
684       dst="${destination}/${src##*/}"
685       mkdir -p "${dst}"
686       paranoidCp "${src}"/* "${dst}"
687     fi
688   done
689 )
690
691 paranoidCopyFile()
692 (
693   #copy a single file to a target in an existing dir
694   #repeat a few times if copy fails
695   #returns 1 on failure, 0 on success
696   src=$(get_realpath "${1}")
697   dst=$(get_realpath "${2}")
698   [[ -d "${dst}" ]] && dst="${dst}/${src##*/}"
699   #some sanity check
700   [[ -z "${src}" ]] && echo "$1 does not exist" && return 1
701   [[ -z "${dst}" ]] && echo "$2 does not exist" && return 1
702   #check if we are not trying to copy to the same file
703   [[ "${src}" == "${dst}" ]] && echo "$dst==$src, not copying" && return 0
704   ok=0
705   [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
706
707   echo "paranoid copy started: $src -> $dst"
708   for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
709
710     echo "...attempt $i of $maxCopyTries"
711     rm -f "$dst"
712     cp "$src" "$dst"
713
714     cmp -s "$src" "$dst"
715     if [ $? == 0 ] ; then
716       ok=1
717       break
718     fi
719
720   done
721
722   if [[ "$ok" == 1 ]] ; then
723     echo "paranoid copy OK after $i attempt(s): $src -> $dst"
724     return 0
725   fi
726
727   echo "paranoid copy FAILED after $maxCopyTries attempt(s): $src -> $dst"
728   return 1
729 )
730
731 generPWD(){ 
732   #
733   # generate semirandom pwd using 2 keys 
734   # Example usage:
735   # generPWD  myserviceaccount10 key11
736   key0=$1
737   key1=$2
738   heslo0=`md5sum <<< "$key0 $key1" | cut -c 1-16`
739   heslo=`echo $heslo0 | cut -c 1-8| awk '{print toupper($0)}'`
740   heslo=$heslo`echo $heslo0 | cut -c 8-15| awk '{print tolower($0)}'`%
741   echo $heslo;
742 }
743
744 #this makes debugging easier:
745 #executes the command given as an argument in this environment
746 #use case:
747 #  bashdb utilities.sh summarizeLogs * */*
748 [[ $# != 0 ]] && eval "$@"