some 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #! /bin/bash
  2. # Script to print "some" lines of a file, like head or tail but, random start
  3. #
  4. # Usage: some [[-#] FILE]
  5. set -e; set -u; set -o pipefail # be safe out there
  6. # Helper functions
  7. PROG=`basename "$0" | tr -d '\n'`
  8. function info() { echo `date +%c` ${PROG}\: info: "$@" 1>&2; }
  9. function warn() { echo `date +%c` ${PROG}\: warning: "$@" 1>&2; }
  10. function error() { echo `date +%c` ${PROG}\: error: "$@" 1>&2; }
  11. function debug() { [[ -v DEBUG ]] && echo `date +%c` ${PROG}\: debug: "$@" 1>&2 || true ; }
  12. function die() { echo `date +%c` ${PROG}\: fatal: "$@" 1>&2 && exit 1; }
  13. function truncate() {
  14. # trunchate a FILE to MAXLINES
  15. local FILE=$1
  16. local MAXLINES=$2
  17. local TEMP=`mktemp`
  18. cp ${FILE} ${TEMP} && \
  19. tail -${MAXLINES} ${TEMP} > \
  20. ${FILE} && \
  21. rm ${TEMP}
  22. }
  23. function someLines {
  24. # Functon to print "some" lines of a file, like head or tail but, random start
  25. #
  26. # ARGS:
  27. #
  28. # $1 - "-#" or FILENAME if only one arg
  29. # $2 - FILENAME if present
  30. local FILE=$1
  31. local HOW_MANY=$2
  32. # Count the lines to bound display
  33. LINES=`wc -l $FILE | sed -e 's/ .*//'`
  34. # TODO Do something sensible for stdin here
  35. # Either
  36. # - Save /dev/stdin to a tmp file (up to a max?), count THAT and use as FILE
  37. # - revert to head-like behavior (with a timeout?) (since we have no idea how long stdin is)
  38. # pick a random starting line at least HOW_MANY back from the end
  39. FIRST=$((1 + RANDOM % (LINES - HOW_MANY + 1)))
  40. FIRST=$((FIRST>LINES ? LINES : FIRST))
  41. LAST=$((FIRST + HOW_MANY - 1))
  42. LAST=$((LAST>LINES ? LINES : LAST))
  43. # log line numbers to allow re-extraction of randomly chosen lines
  44. info lines $FIRST to $LAST \(of $LINES\) of `readlink -f $FILE` |& cat >> ${SOMELOG}
  45. # only keep MAXLOG lines of SOMELOG
  46. truncate $SOMELOG $MAXLOG
  47. # Lets see some lines !
  48. awk "NR >= $FIRST && NR <= $LAST" $FILE
  49. }
  50. function usage() {
  51. if [ "$1" != "" ]; then
  52. # TODO allow for undefined $1. Now, have to set to "" or get undefined error.
  53. warn "${1}"
  54. fi
  55. USAGE=$(cat <<-END
  56. Usage: $PROG [options] [FILENAME]
  57. Print some random lines from a file
  58. options:
  59. -# The number of lines to print. Default 10.
  60. -h Print usage.
  61. arguments:
  62. FILENAME The file to sample. Default /dev/stdin
  63. END
  64. )
  65. info "$USAGE"
  66. }
  67. function main() {
  68. # main - parse args, set defaults, call someLines
  69. # Bash Version 3 required (it also works with ksh)
  70. [[ ${BASH_VERSINFO[0]} -lt 3 ]] && exit 1
  71. # Defaults
  72. local HOW_MANY=10 # Number of lines to print. Default.
  73. local SOMELOG="${HOME}/.somelog" # log of queries in case you want to find that chunk again
  74. local MAXLOG=10
  75. # Put all arguments in a new array (because BASH_ARGV is read only)
  76. ARGS=( "$@" )
  77. for i in "${!ARGS[@]}"; do
  78. case "${ARGS[i]}" in
  79. '') # Skip if element is empty (happens when its unsetted before)
  80. continue
  81. ;;
  82. -[0-9]*) # Use +1 to access next array element and unset it
  83. HOW_MANY="${ARGS[i]}"
  84. HOW_MANY=`echo $HOW_MANY | sed 's/^-//'`
  85. shift
  86. ;;
  87. -h) # Use +1 to access next array element and unset it
  88. usage ""
  89. exit 0
  90. ;;
  91. --) # End of arguments
  92. unset 'ARGS[i]'
  93. break
  94. ;;
  95. *) # Skip unset if our argument has not been matched
  96. continue
  97. ;;
  98. esac
  99. unset 'ARGS[i]'
  100. done
  101. # TODO add -n argumet to print line numbers
  102. FILE=${1:-"/dev/stdin"}
  103. if [ "$#" -gt 1 ]; then
  104. usage "Too many arguments"
  105. exit 1
  106. fi
  107. someLines $FILE $HOW_MANY
  108. }
  109. main $*