#! /bin/bash # Script to print "some" lines of a file, like head or tail but, random start # # Usage: some [[-#] FILE] set -e; set -u; set -o pipefail # be safe out there # Helper functions PROG=`basename "$0" | tr -d '\n'` function info() { echo `date +%c` ${PROG}\: info: "$@" 1>&2; } function warn() { echo `date +%c` ${PROG}\: warning: "$@" 1>&2; } function error() { echo `date +%c` ${PROG}\: error: "$@" 1>&2; } function debug() { [[ -v DEBUG ]] && echo `date +%c` ${PROG}\: debug: "$@" 1>&2 || true ; } function die() { echo `date +%c` ${PROG}\: fatal: "$@" 1>&2 && exit 1; } function truncate() { # trunchate a FILE to MAXLINES local FILE=$1 local MAXLINES=$2 local TEMP=`mktemp` cp ${FILE} ${TEMP} && \ tail -${MAXLINES} ${TEMP} > \ ${FILE} && \ rm ${TEMP} } function someLines { # Functon to print "some" lines of a file, like head or tail but, random start # # ARGS: # # $1 - "-#" or FILENAME if only one arg # $2 - FILENAME if present local FILE=$1 local HOW_MANY=$2 # Count the lines to bound display LINES=`wc -l $FILE | sed -e 's/ .*//'` # TODO Do something sensible for stdin here # Either # - Save /dev/stdin to a tmp file (up to a max?), count THAT and use as FILE # - revert to head-like behavior (with a timeout?) (since we have no idea how long stdin is) # pick a random starting line at least HOW_MANY back from the end FIRST=$((1 + RANDOM % (LINES - HOW_MANY + 1))) FIRST=$((FIRST>LINES ? LINES : FIRST)) LAST=$((FIRST + HOW_MANY - 1)) LAST=$((LAST>LINES ? LINES : LAST)) # log line numbers to allow re-extraction of randomly chosen lines info lines $FIRST to $LAST \(of $LINES\) of `readlink -f $FILE` |& cat >> ${SOMELOG} # only keep MAXLOG lines of SOMELOG truncate $SOMELOG $MAXLOG # Lets see some lines ! awk "NR >= $FIRST && NR <= $LAST" $FILE } function usage() { if [ "$1" != "" ]; then # TODO allow for undefined $1. Now, have to set to "" or get undefined error. warn "${1}" fi USAGE=$(cat <<-END Usage: $PROG [options] [FILENAME] Print some random lines from a file options: -# The number of lines to print. Default 10. -h Print usage. arguments: FILENAME The file to sample. Default /dev/stdin END ) info "$USAGE" } function main() { # main - parse args, set defaults, call someLines # Bash Version 3 required (it also works with ksh) [[ ${BASH_VERSINFO[0]} -lt 3 ]] && exit 1 # Defaults local HOW_MANY=10 # Number of lines to print. Default. local SOMELOG="${HOME}/.somelog" # log of queries in case you want to find that chunk again local MAXLOG=10 # Put all arguments in a new array (because BASH_ARGV is read only) ARGS=( "$@" ) for i in "${!ARGS[@]}"; do case "${ARGS[i]}" in '') # Skip if element is empty (happens when its unsetted before) continue ;; -[0-9]*) # Use +1 to access next array element and unset it HOW_MANY="${ARGS[i]}" HOW_MANY=`echo $HOW_MANY | sed 's/^-//'` shift ;; -h) # Use +1 to access next array element and unset it usage "" exit 0 ;; --) # End of arguments unset 'ARGS[i]' break ;; *) # Skip unset if our argument has not been matched continue ;; esac unset 'ARGS[i]' done # TODO add -n argumet to print line numbers FILE=${1:-"/dev/stdin"} if [ "$#" -gt 1 ]; then usage "Too many arguments" exit 1 fi someLines $FILE $HOW_MANY } main $*