30

Under a UNIX shell, how can I get a similar effect to the watch command, but with paging so that I can scroll around in the output if it takes up more than one screen?

In other words, I want a program that is to watch what less is to cat.

As an example, lets say I wanted to watch the output of qstat, I could use

watch qstat

to watch the output of qstat, but this can only shows the first screenful.

With a paging version of watch, I would be able to move around in the output as it is still continuously updated by watch. Is there any way to do this at the moment with existing utilities?

David Dean
  • 441
  • 6
  • 11

13 Answers13

21

Rather than modifying the 'watch' command, use screen!

For example, let's say that you need to be able to see 300 lines of height and 100 characters of width and move around that. After starting screen, force the size thus:

C-a :height -w 300
C-a :width -w 100

Now start your watch command. You can then use C-a <ESC> to page around the display.

Unfortunately, the display doesn't refresh while in copy mode. But if you want to adjust which section of the window you're viewing, the easiest way may be to rerun the height/width commands as by default your terminal shows the lower-right of the virtual window.

henry
  • 103
  • 4
MikeyB
  • 38,725
  • 10
  • 102
  • 186
7

You can try this:

$ while vmstat; do sleep 1; done | less

replace vmstat with qstat and adjust the sleep to your needs.

slm
  • 7,355
  • 16
  • 54
  • 72
Spacen Jasset
  • 234
  • 1
  • 8
  • all this does is keep repeating the command into `less`, which means that you need to keep scrolling to see the latest output. – David Dean Aug 14 '09 at 06:32
  • 1
    Yes, but you can scroll back. You can't have both at once. Pressing shift f, that is capital 'F' will work like tail. – Spacen Jasset Aug 14 '09 at 14:06
  • 1
    Append `+F` to "pre-press" F and automatically follow: `while vmstat; do sleep 1; done | less +F` – rymo Jan 04 '20 at 03:06
4

Multitail: http://www.vanheusden.com/multitail/

Example:

 vmstat 1 |multitail -j

Scroll back by press 'b' and page/arrow up/down.

Lester Cheung
  • 659
  • 4
  • 11
2

OK, I've had a little go at a watchless function. It's a bit rough, and it doesn't yet appear to completely work, but here goes:

#!/bin/bash -u
out=$(mktemp)
(while [ 1 ]; do
    "$@" > $out;
    sleep 2;
done) &
less $out
kill $!

You have to manually use the R key in less to get the display to update.

It appears to work for watchless date but not for watchless qstat or watchless pstree, which both show blank. Any ideas?

David Dean
  • 441
  • 6
  • 11
  • You should use a second file to write the output of the next run of the command and then `mv` this second file to the file read by `less` as `mv` is atomic. Otherwise `less` could think that the file has disappeared if a refresh is requested at the same time as the file is being written to with `"$@" > $out`. You should also write to the output file once before starting the loop. – Mmmh mmh Aug 28 '16 at 19:28
0

I am using this:

while cat /etc/wgetrc; do sleep 10; done | pv -q -L 150 | tail -n +0 -f
fabceolin
  • 101
  • 1
0

I edited the script here to work with command line

#!/bin/bash
#
# watch a file and scroll
#
# keys => arrow-up/down, page-up/down, pos1, end
#
# usages:
#           swatch -n <timeout_watch> <file>
#           swatch <file>
#
# version:          1.1
# dependencies:     awk , tput , clear, read
# published:        https://unix.stackexchange.com/questions/3842/how-can-i-scroll-within-the-output-of-my-watch-command
# gif recording:    peek , https://github.com/phw/peek

#
# =============================================
# KEYCODES
# =============================================
# https://unix.stackexchange.com/questions/294908/read-special-keys-in-bash
# showkey -a


# =============================================
# DEFAULTS
# =============================================
command=""
TMPFILE=$(mktemp)
line_show_begin=1
line_show_begin_last=-1
console_lines_correction=4
timeout_watch=5
timeout_read=.1


# =============================================
# DEFINE Escape-Sequences
# =============================================

# http://ascii-table.com/ansi-escape-sequences-vt-100.php

ESC_clr_line='\033[K'
ESC_reset_screen='\033c'
ESC_clr_screen='\033[2J'
ESC_cursor_pos='\033[0;0f'
ESC_cursor_home='\033[H'


# =============================================
# FUNCTIONS
# =============================================


function fn_help() {
cat << EOF
Usage: ./$0 [-n <timeout>] [<command>]  ,  timeout >0.1s , default 5s
EOF
}


function get_options() {
    [[ "$1" == "" ]] && { fn_help ; exit 1 ; }
    while [ -n "$1" ]; do
        case "$1" in
            -h|--help)
                fn_help
            ;;
            -n)
                [[ "$2" == "" ]] && { echo "Error: option -n required <timeout>" ; exit 1 ; }
                if [[ "$(echo "$2<0.1"|bc)" == "0" ]] ; then
                    timeout_watch="$2"
                    shift
                else
                    echo "Error: timeout <0.1 not allowed"
                    exit 1
                fi
            ;;
            -*)
                echo "Error: unknown option »$1«"
                exit 1
            ;;
            *)
                #if [[ -f "$1" ]] ; then
                command=$1
                #else
                #    echo "Error: file not found »$1«"
                #    exit 1
                #fi
            ;;
        esac
        shift
    done
    [[ "$command" == "" ]] && { echo "Error: command required" ; exit 1 ; }
}


function fn_print_headline() {
    hdl_txt_right="${HOSTNAME}: $(date "+%Y-%m-%d %H:%M:%S")"
    hdl_txt_left="$command , ${timeout_watch}s , $line_show_begin"
    hdl_txt_left_length=${#hdl_txt_left}
    printf '%s%*s\n\n' "$hdl_txt_left" "$(($console_columns-$hdl_txt_left_length))" "$hdl_txt_right"
}


function fn_print_file() {
    # ---------------------------------------------------
    # file lenght can change while watch
    # ---------------------------------------------------
    eval $command > $TMPFILE
    lines_command=$(awk 'END {print NR}' $TMPFILE)
    line_last=$(($lines_command-$console_lines))
    (( "$line_last" < "1" )) && { line_last=1; clear; }
    (( "$line_show_begin" > "$line_last" )) && { line_show_begin=$line_last; clear; }

    # ---------------------------------------------------
    # print postion changed
    # ---------------------------------------------------

    if (( "$line_show_begin" != "$line_show_begin_last" )) ; then
        line_show_begin_last=$line_show_begin;
        clear
    else
        printf $ESC_cursor_home
    fi

    # ---------------------------------------------------
    # print file section
    # ---------------------------------------------------

    fn_print_headline
    eval $command > $TMPFILE
    awk -v var1="$line_show_begin" -v var2="$console_lines" 'NR>=var1 {if (NR>var1+var2) {exit 0} else {printf "%s\n",$0 } }' $TMPFILE
}


function fn_console_size_change() {
    console_columns=$(tput cols)
    console_lines=$(($(tput lines)-$console_lines_correction))
    line_show_begin_last=-1
}


function fn_quit() {
    echo "quit" $0 , $?
    setterm -cursor on ; exit 0
}


# =============================================
# GET OPTIONS
# =============================================

get_options "$@"    # pass all arguments with double-quotes



# =============================================
# INIT TRAP
# =============================================

trap "fn_console_size_change" SIGWINCH # https://en.wikipedia.org/wiki/Signal_(IPC)#SIGWINCH
trap "fn_quit" INT TERM EXIT


# =============================================
# MAIN
# =============================================

fn_console_size_change
setterm -cursor off


while true ; do
    fn_print_file
    read -rsn1 -t $timeout_watch k # char 1
    case "$k" in
        [[:graph:]])
            # Normal input handling
        ;;
        $'\x09') # TAB
            # Routine for selecting current item
        ;;
        $'\x7f') # Back-Space
            # Routine for back-space
        ;;
        $'\x01') # Ctrl+A
            # Routine for ctrl+a
        ;;
        $'\x1b') # ESC
            read -rsn1 k # char 2
            [[ "$k" == ""  ]] && return  Esc-Key
            [[ "$k" == "[" ]] && read -rsn1 -t $timeout_read k # char 3
            [[ "$k" == "O" ]] && read -rsn1 -t $timeout_read k # char 3
            case "$k" in
                A)  # Arrow-Up-Key
                    (( "$line_show_begin" > "1" )) && line_show_begin=$(($line_show_begin-1))
                ;;
                B)  # Arrow-Down-Key
                    (( "$line_show_begin" < "$line_last" )) && line_show_begin=$(($line_show_begin+1))
                ;;
                H)  # Pos1-Key
                    line_show_begin=1
                ;;
                F)  # End-Key
                    line_show_begin=$line_last
                ;;
                5)  # PgUp-Key
                    read -rsn1 -t $timeout_read k # char 4

                    if [[ "$k" == "~" ]] && (( "$line_show_begin" > "$(($console_lines/2))" )) ; then
                        line_show_begin=$(($line_show_begin-$console_lines/2))
                    else
                        line_show_begin=1
                    fi
                ;;
                6)  # PgDown-Key
                    read -rsn1 -t $timeout_read k # char 4
                    if [[ "$k" == "~" ]] && (( "$line_show_begin" < "$(($line_last-$console_lines/2))" )) ; then
                        line_show_begin=$(($line_show_begin+$console_lines/2))
                    else
                        line_show_begin=$line_last
                    fi
                ;;
            esac
            read -rsn4 -t $timeout_read    # Try to flush out other sequences ...
        ;;
    esac
done
  • create a file in ~/bin/cwatch.sh
nano ~/bin/cwatch.sh
  • change files properties to make it runnable:
chmod +x ~/bin/cwatch.sh
  • edit ~/.bashrc and add alias
alias cwatch="~/bin/cwatch.sh"

now you can try it:

cwatch 'ps aux | grep -v grep'
0

In case this could be useful to others, this is how I solved the issue on my side. In the "cmd" function, plug any command you want to monitor.

#!/bin/bash

function cmd {
  sudo lsof -i -n -P | grep 11.12.13.14:8883 
}

echo "" >previous.log
while true; do 
  cmd >out.log  
  diff out.log previous.log >/dev/null 
  cc="$?"
  if [[ "$cc" != "0" ]]; then
    date=`date --rfc-3339=seconds`
    epoch=`date +%s`
    echo ""
    echo "Changes on $date ($epoch)" 
    cat out.log
    cp out.log previous.log
  fi
  sleep 0.5
done    
0

I don't see how this could be implemented as the row contents change, and watch would reset back to first line every 2 seconds even if you could scroll down.

Some workarounds are:

watch 'qstat | tail -n40' to show output of qstat beginning from 40th line from bottom

watch 'qstat | grep jsmith' to grep the output so the lines you are interested in are always in the first screen.

Note that you need to put the commands around the pipe in single quotes - otherwise you will be piping the output of watch, not the output of qstat.

enkrs
  • 121
  • 1
  • 1
  • 7
0

To continue on enkrs's answer,

watch 'qstat | head -300 | tail -15'

will get you arbitrary pages into the qstat's output.

0

Here's a rather crude script that seems to work for several commands that I threw at it

#!/bin/bash
# ---- mywatch.sh ----

if [ $# -lt 1 && $# -gt 2 ]; then
    echo "Usage: $0 <command> <delay>" 
    exit 1
fi

CMD=$1
if [ $# -eq 2 ]; then
  DELAY=$2
else
  DELAY=2 # default
fi

while : ; do
  ( (echo -e "Every ${DELAY}s: $CMD\n"; $CMD) | less )&
  PID=$!
  sleep $DELAY
  kill -9 $PID &> /dev/null
  clear
done

Used as such:

alias mywatch="~/bin/mywatch.sh"

mywatch vmstat
mywatch "ps aux" # commands in options need to be quoted
mywaych pstree 10 # change delays

Being rather pedantic, the transition between refreshes aren't as smooth as I would like it to be. Naturally, being a simple script it doesn't support highlight of diff (watch -d). Also, the parsing of input arguments can be done better.

Shawn Chin
  • 1,804
  • 11
  • 12
0

How about just: tail -f

geeklin
  • 518
  • 2
  • 10
0

I implement a simple python script to satisfy this request, named "watchall"

get it by: pip install watchall

replace watch with watchall and enjoy scrollable screen. now it only supports -n and -d flags.

-1

you can try:

watch command > file

then in your file you should see the appendend output (I don't have a linux box rigth now to test this)