简体   繁体   中英

Linux CLI watch switch -e, --errexit unexpected exit with “command exit with a non-zero status, press a key to exit”

Linux CLI watch command has a switch -e , --errexit that has a description:

Freeze updates on command error, and exit after a key press.

That switch should make watch stop executing command if it returned non zero exit code.

The problem is that watch terminates if the output of command does not fit into the CLI window. The problem is present sometimes when the CLI window is full screen on Ubuntu 18.04 and always when you resize window or it is smaller than full screen.

An example of script having commands:

task.sh

#!/bin/sh

for i in $(seq 1 200)
do
    printf "Task #${i}\n";
done

exit 0;

and watch command:

watch -e -n 1 ./task.sh;

watch unexpected error:

command exit with a non-zero status, press a key to exit

How to solve that problem? I can't redirect standard output to /dev/null because at least partial output of the commands that are executed with watch needs to be printed and watch should terminate if any command of the executed script returns non zero exit code so I guess I'm forced to use -e , --errexit switch.

If there is no good solution to that specific watch behavior, is there any good replacement for watch ?

EDIT

It looks like watch problem is rather related to multiple commands print exceeding visible terminal output than the the total of printed string. This example uses single printf and does not have any problem when resizing terminal screen even it prints more than the visible part of the terminal:

#!/bin/sh

output="";
for i in $(seq 1 200)
do
    output="${output}$(printf "Task #${i}")\n";
done

printf "$output";
exit 0;

but this hack can work with relative small scripts, I can't imagine using watch and doing this workaround for every command inside task.sh srcipt

Another workaround:

#!/usr/bin/env bash

temp="$(mktemp /tmp/watch.XXXXXXX)"
exec 3>&1;  exec > $temp

date # for demo purpose
for i in $(seq 1 200)
do
    printf "Task #${i}\n";
done

exec 1>&3
cat $temp; rm $temp

So you don't need to change much the original bash script.

I cannot think of way using variable, if tmp file is really an issue, try this:

#!/usr/bin/env bash

{

date # for demo purpose
for i in $(seq 1 200)
do
    printf "Task #${i}\n";
done

} | head -n $LINES

Task wrapper for watch with -e switch (exit on error).
Does not have any problem when resizing terminal window, even down to just 1 line.
Optionally may save stdout and stderr into a file.

Works as #!/bin/bash or #!/bin/sh

task-wrapper.sh

#!/bin/sh

# path to file where to put stdout and stderr on error
error_log="$1";

output="$(
    # exit sub shell with non zero code on 
    # any line in sub shell having non zero exit code
    set -e; 

    {
        # ========== Commands Block =============

        # uncomment below to simulate an error
        # ls /this/is/non/existing/path;

        # an example of lot of print exceeding
        # count of displayed terminal lines
        for i in $(seq 1 200)
        do
            printf "Task #${i}\n";
        done

        # uncomment below to simulate an error
        # ls /this/is/non/existing/path;

        # ========== Commands Block =============

    } 2>&1;
)";

# get sub shell exit code
# print output (limit to terminal capacity)
# save error if any
# exit with error code if any
# or exit 0;

ec="$?"; if [ $ec -eq 0 ]; then 
    # zero exit code

    # prevent echo if there are only two
    # lines of terminal available because
    # they may be already consumed by watch
    # and its status if watch run without 
    # --no-title switch (default)
    if [ $LINES -gt 2 ]; then
        echo "$output" | tail -n "$(($LINES - 2))";
    fi;

    exit 0; 

else
    # non zero exit code

    # watch on error
    # consumes additional one line
    # therefore don't echo if lines
    # available are less than 4
    if [ "$LINES" -gt 3 ]; then 
        echo "$output" | tail -n "$(($LINES - 3))";
    fi;

    # watch erases terminal
    # output after press of any key 
    # and its teminal print has no scroll
    # so save whole stdout and stderr to file
    # if path to it was provided
    if [ -n "$error_log" ]; then
        echo "$output" > "$error_log";
    fi;

    # exit with sub shell exit code
    exit "$ec"; 
fi;

Usage:

watch -e ./task-wrapper.sh

Usage with path to file created as error log

watch -e ./task-wrapper.sh ./task-error.log

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM