#!/bin/bash export LC_ALL=C export POSIXLY_CORRECT=yes printUsage() { cat <<-EOF Usage: $(basename "$0") [-h|--help] [FILTER] [COMMAND] [LOG [LOG2 [...]] COMMAND: list-tick list all tickers mentioned in the logs profit final product of all entries (negative for a net loss) pos currently held positions ordered asc. by value last-price last hist-ord histogram of transactions grouped by tickers graph-pos graph positions for each ticker [ommited] entries are echoed according to the provided filters (if any) FILTER: -a DATETIME only records AFTER (exclusive) this date are considered -b DATETIME only records BEFORE (exclusive) this date are considered format: YYYY-MM-DD HH:MM:SS or in POSIX: %Y-%m-%d %H:%M:%S -t TICKER count records for this TICKER, can be repeated -w WIDTH sets the width (in columns) of the longest line for commands hist-ord - if ommited, one block = one transaction graph-pos - if ommited, one block = 1000 units of currency Note: When printing blocks of the graph, division products are floored (rounded towards zero). So for \`graph-pos -w 6\` with 1234 (equal to 6 blocks) as the maximum value, 1233.99 will be shown as #####. Format: DATUM A CAS;TICKER;TYP TRANSAKCE;JEDNOTKOVA CENA;MENA;OBJEM;ID Exit codes: 0 Success. 1 Invalid date provided for the -a or -b filter. 2 Duplicated parameter. EOF exit 1 } ARG_DATE_AFTER="0000-01-01 00:00:00" ARG_DATE_BEFORE="9999-12-31 23:59:59" ARG_TICKER="" ARG_WIDTH="0" ARG_COMMAND="echo" # needs spaces around all quotes ACCEPTED_COMMANDS=" list-tick profit pos last-price hist-ord graph-pos echo " # validateDateFormat (date) # Returns 0 if the provided string follows YYYY-MM-DD HH:MM:SS. # Note: This is a veeeeeeeeeeery basic check, allows e. g. "2020-12-24 15" function validateDateFormat { # accepts 60 for leap seconds awkOut="$(echo "$1" | awk -v FS='[-: ]' -v RS='\0' ' { print (match($0, /^[[:digit:]]{4,}-[[:digit:]]{2,}-[[:digit:]]{2,} [[:digit:]]{2,}:[[:digit:]]{2,}:[[:digit:]]{2,}\n?$/)) ? "good" : "bad" }')" [ "$awkOut" = "good" ]; } # processFile (file) function processFile { catcmd="cat" if [ "${1: -3}" == ".gz" ]; then catcmd="zcat" fi aftercmd="cat" # it could be done in awk, but so far we managed without GNU extensions, # hence we'll bite our tongue and just do this case "$ARG_COMMAND" in list-tick ) aftercmd="sort -dk1" ;; pos ) aftercmd="sort -nk3 -r" ;; last-price ) aftercmd="sort -dk1" ;; hist-ord ) aftercmd="sort -dk1" ;; esac "$catcmd" "$1" | awk \ -f "$ARG_COMMAND.awk" \ -v ARG_TICKER="$ARG_TICKER" \ -v ARG_DATE_AFTER="$ARG_DATE_AFTER" \ -v ARG_DATE_BEFORE="$ARG_DATE_BEFORE" \ -v ARG_WIDTH="$ARG_WIDTH" \ - | $aftercmd # Note: Oh wouldn't it be great to use the @include keyword # from GNU awk and avoid all that duplicated code. Oh well. } # Transform long options into short ones for arg in "$@"; do shift case "$arg" in "--help") set -- "$@" "-h" ;; *) set -- "$@" "$arg" esac done _hasSpecifiedWidth=0 if [ ! $1 ]; then cat - exit 0 fi while getopts "ha:b:t:w:" o; do case "$o" in a) ARG_DATE_AFTER="$OPTARG" if ! validateDateFormat "$OPTARG"; then echo "Invalid after date." 1>&2 exit 1 fi ;; b) ARG_DATE_BEFORE="$OPTARG" if ! validateDateFormat "$OPTARG"; then echo "Invalid before date." 1>&2 exit 1 fi p="$OPTARG" ;; t) ARG_TICKER="$ARG_TICKER;$OPTARG" # if [ ! "$OPTARG" ]; then echo "Missing argument for -t" ; exit 2 ; fi ;; w) ARG_WIDTH="$OPTARG" if [ $_hasSpecifiedWidth = "1" ]; then echo "Width specified twice!" 1>&2 exit 2 fi _hasSpecifiedWidth=1 ;; h) printUsage ;; *) ;; esac done shift "$((OPTIND-1))" # otherwise fails with "binary operator expected" ?!?!?! _tmpArgs="$@" if [ -z "$_tmpArgs" ]; then processFile - fi for arg in "$@"; do if [ -z "${ACCEPTED_COMMANDS##* $arg *}" ]; then ARG_COMMAND="$arg" shift fi done for arg in "$@"; do processFile "$arg" done