: ########################################################################## # Title : nicecol - print "nice" columnar output # Author : Heiner Steven # Date : 1995-12-10 # SCCS-Id. : @(#) nicecol 1.7 20/12/05 ########################################################################## # Changes # 1995-12-21 stv Allow whitespace in output field separator (0.2) # (logan@ptolemy.arc.nasa.gov) # 1999-07-13 stv Remove trailing whitespace characters (1.2) # 2001-04-18 stv Handle columns with large numbers (digits>9) (1.5) # 2005-04-12 stv Handle regular expressions for input field separators # (thanks to cshobe@seattleserver.com for finding the bug) # 2020-12-05 stv New option -c for ignoring comment lines # Ignore (pass through) empty lines ########################################################################## PN=`basename "$0"` # Program name VER='1.7' if [ -z "$NAWK" ] then for awk in mawk gawk nawk do for path in . `echo "$PATH" | tr : ' '` do [ -x "$path/$awk" ] || continue NAWK=$path/$awk break 2 done done fi : ${NAWK:=awk} Usage () { echo >&2 "$PN - produce \"nice\" columnar output, $VER (stv '95) usage: $PN [-i IFS] [-o OFS] [-c comment] [file ...] -c: ignore lines starting with comment pattern -i: input field separator (default: whitespace) -o: output field separator (default: blank) Depending on the AWK version installed, the input field separator and the comment pattern could be an extended regular expression (see $NAWK(1)): $PN -i '[.,;]+'" exit 1 } #set -- `getopt 'hc:i:o:' "$@"` || Usage while [ $# -gt 0 ] do case "$1" in -c) Comm="$2"; shift;; # Comment character -i) ISep="$2"; shift;; # Input field separator -o) OSep="$2"; shift;; # Output field separator --) shift; break;; -h) Usage;; -*) Usage;; *) break;; # first file name esac shift done Data=${TMPDIR:=/tmp}/nc.data.$$ Format=$TMPDIR/nc.fmt.$$ trap 'rm -f "$Data" "$Format" >/dev/null 2>&1' 0 trap "exit 2" 1 2 3 13 15 # First pass: determine field types and sizes cat "$@" | tee "$Data" | $NAWK ${ISep+-F"$ISep"} ' function Max (a, b) { if ( a > b ) return a; else return b; } # Ignore comments? "'"$Comm"'" != "" && $1 ~ /'"$Comm"'/ { next } # Ignore empty lines $1 == "" { next } { for ( i=1; i<=NF; i++ ) { if ( Type [i] == "string" ) { Width [i] = Max(Width [i], length ($i)) } else { if ( $i ~ /^[0-9][0-9]*$/ && Type [i] != "float" ) { Type [i] = "int" Width [i] = Max(Width [i], length ($i)) Prec [i] = 0 } else if ( $i ~ /^[0-9][0-9]*\.[0-9]*$/ || $i ~ /^[0-9]*\.[0-9][0-9]*$/ ) { Type [i] = "float" k = split ($i, V, ".") Width [i] = Max(length (V [1]), Width [i]) Prec [i] = Max(length (V [2]), Prec [i]) } else { Type [i] = "string" Width [i] = length ($i) } } } if ( NF > MaxFields ) MaxFields = NF } END { for ( i=1; i<=MaxFields; i++ ) { if ( Type [i] == "string" ) { print "%-" Width [i] "s" } else if ( Type [i] == "int" ) { if ( Width [i] < 10 ) { print "%" Width [i] "d" } else { # Special handling: a decimal number cannot have # more than 9 digits; otherwise it would be printed # in exponential notation print "%" Width [i] "s" } } else if ( Type [i] == "float" ) { print "%" Width[i]+Prec[i]+1 "." Prec[i] "f" } } } ' > "$Format" || exit $? #sed "s/^/ F /" "$Format" >&2 # Second pass: format input $NAWK ${ISep+-F"$ISep"} ' BEGIN { Delim = "'"$OSep"'" if ( Delim == "" ) Delim = " " # Read format specification for each field for ( n=1; getline < "'$Format'"; n++ ) Fmt [n] = $0 close ("'$Format'") } $1 == "" { print; next } "'"$Comm"'" != "" && $1 ~ /'"$Comm"'/ { print; next } { for ( i=1; i<=NF; i++ ) { printf "" Fmt [i], $i if ( i