1

I am trying to write a bash script that will run a second script repeatedly until it fails and capture the stdout and stderr of that second script. I have managed to deal with most problems encountered during the process, and have ended up with this script (called error_checker.sh):

#!/usr/bin/env bash

i=0

while [ $? -ne 1 ]
do
i=$[$i+1]
source script.sh &> log.txt
echo "This error occurred after $i run(s)." >> log.txt
done

The script I would like to repeat repeatedly is called script.sh, and has the following code:

#!/usr/bin/env bash

 n=$(( RANDOM % 100 ))

 if [[ n -eq 42 ]]; then
    echo "Something went wrong"
    >&2 echo "The error was using magic numbers"
    exit 1
 fi

 echo "Everything went according to plan"

The error_checker.sh script can output the stdout and stderr of that failed run to a designated file, log.txt, but as the script exits along with the second script at the line

source script.sh &> log.txt

I can not append the additional error message to that file due to this.

I have searched around for a while and found that this might be related to creating subshells when running the second script, which may solve the problem, but I just could not get the script to run using ./ instead of source. If I changed source to ./, the whole terminal just froze when I type in ./error_checker.sh.

Much appreciated to any helping hand!

UPDATE: Thanks for the current answers! I think that now my main question is that when I change source script.sh to ./script.sh in the error_checker.sh to run it in a new shell process the whole terminal just froze, which prevents me from using ./, and that is why I could only run error_checker.sh successfully when using source script.sh. Any ideas what happened?

UPDATE II: I have changed source to ./ and found that although the whole terminal seems frozen and I had to end the process by pressing ctrl + c, it can still generate a log.txt. However, while the version using source will generate a log.txt saying Something went wrong. The error was using magic numbers, which indicates the stdout and stderr were captured by using &>, the version using ./ generated a log.txt saying Everything went according to plan. The error occurred after X run(s). Any idea what happened?

FINAL UPDATE: I have edited my code according to the answer by Glenn to the following and it works perfectly. Seems that the echo command in the while loop has blocked me from actually evaluating the exit status of script.ph, and thus creating an infinite loop.

#!/usr/bin/env bash

i=0

while [ $? -ne 1 ]
do
((i++))
./script.sh >& log.txt
done

echo "This error occurred after $i run(s)." >> log.txt
2
  • 2
    Why are you sourcing script.sh instead of just running it?
    – fpmurphy
    Nov 23 '20 at 14:12
  • I tried to just run it by using ./script.ph but the whole terminal just froze. Do not really know what happened.
    – HAI LIN
    Nov 24 '20 at 1:48
0

Because you're sourcing the script, change exit 1 to return 1

If you also plan to run script.sh as a "top level" script, you can do

if (( n == 42 )); then
    if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then
        # I'm the "main" script
        exit 1
    else
        # I'm being sourced from some other script
        return 1
    fi
fi

If you must use source (this I doubt, but we'd have to see the code) and you cannot alter that script, then you must perform the source command in a subshell. This means that the exit will exit from the subshell, not your current shell that is running the toplevel program:

i=0
#     /--------------------------------\  parentheses create a subshell
while (source script.sh >> log.txt 2>&1)
do
    ((i++))
done
echo "This error occurred after $i run(s)." >> log.txt

This creates a log file containing:

Everything went according to plan
...
Everything went according to plan
Something went wrong
The error was using magic numbers
This error occurred after 302 run(s).

Some notes about your code:

  • $[...] is undocumented syntax, possibly deprecated. Use $((...)) for arithmetic expansion, or ((...)) for arithmetic evaluation.
  • $? is the exit status of the previous command, which, for the 2nd iteration of the while loop, is echo. As echo only returns non-zero when it can't write to stdout (or the specified file descriptor), you have an infinite loop because you're not testing the exit status of script.sh!
    • you have a roughly 1 in 100 chance of hitting Ctrl+C after the error condition so 99% of the time the log file will contain the "good" message"
  • &> is not idiomatic: use >& file or the more obvious >file 2>&1.
  • you most likely want to print the "error occurred after ..." message outside of the loop
2
  • I have thought about this by changing the exit 1 to return 1 but for this exercise I am not allowed to change the content of script.ph.
    – HAI LIN
    Nov 24 '20 at 1:54
  • Thanks a lot! I edited a few syntactic problems according to your notes and moved the echo command outside of the file, and not changed anything about the script.sh. I have also switched to ./ from source. Now the whole thing is working perfect and the main shell process is not terminated after encountering error in script.sh. It seems that the major problem is that the echo command is in the while loop.
    – HAI LIN
    Nov 24 '20 at 2:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.