1

I have a.sh that calls a b.sh:

. "b.sh" 2>&1 | tee -a "log.txt" &
wait $!

xxx

how can I write a condition that detects when b.sh is not outputting log.txt for a certain time and then run through wait $!

3
  • 1
    What it means to "run through"? Use a polling method - get length of the file, wait a second, get length of the file - if the lengths are the same, it means nothing was outputted for a certain time. Commented Mar 18, 2020 at 11:39
  • This is risky. If b.sh outputs one character every 15 seconds, it's very likely that tee won't see any data for over 4 hours. (Due to buffering) (This is less likely if b.sh is a shell and likely calling short programs that write output and terminate, but entirely a possibility.) How will you handle that? Commented Mar 18, 2020 at 12:01
  • But bash's read builtin does have a -t option you could use. Commented Mar 18, 2020 at 12:03

3 Answers 3

2

You could do:

#!/bin/bash


./b.sh 2>&1 | { while read -t 3 line; do
    echo "$line" >> log.txt; done; pkill -g 0; } &

wait $!

Note that in your example, your script is not calling b.sh, but is sourcing it. This solution calls b.sh. That seems cleaner, as reading the output of a sourced script is truly strange behavior.

But note that if b.sh is calling some long running program that is generating a lot of output but doing it slowly, you will undoubtedly run into buffering issues. The long running program will very likely be block buffering its output, so although it is generating data periodically into its internal buffers, the read in the while loop will not see it and may timeout prematurely.

Sign up to request clarification or add additional context in comments.

Comments

1

Quite old topic but I've done a mix of all the solutions suggested to fit my need and I think

#!/bin/bash

set -e

rm -rf log.txt

touch log.txt

rm -rf finish

./"b.sh" > log.txt 2>&1 && touch finish &

process_to_keep=$!

{ tail -f log.txt | while read -t 3 line && kill -0 "$process_to_keep" 2>/dev/null; do
    echo "$line"
done;
if [ -e finish ]; then
  echo "finish properly"
else
  if kill -0 "$process_to_keep" 2>/dev/null; then
    echo "no output after 3s"
    kill $process_to_keep 2>/dev/null
  else
    echo "process not alive anymore"
  fi

  exit 1
fi; } &

process_to_wait=$!

wait $process_to_keep
echo "done" >> log.txt
wait $process_to_wait

echo "cleaning temporary files"
rm -rf finish
rm -rf log.txt

This script will stop if the script b exit at any moment (without any delay which was the case with other solution) and it will also exit if there is no output for 3s with a proper message in the console.

I think this solution does not work properly on macOS.

Comments

0
. "b.sh" 2>&1 | tee -a "log.txt" &
child=$!
# loop while child process exists
while kill -0 "$child" 2>/dev/null; then

     # get first length
     if [[ -z "$len1" ]]; then
          len1=$(wc log.txt)
     fi

     # sleep for a certain time
     sleep 1

     # get length of file after 1 second
     len2=$(wc log.txt)

     # compare lengths
     if [ "$len1" == "$len2" ]; then
         echo "Och my! File log.txt did not change in 1 second"
         break
     else
         echo "All good! File log.txt did change in 1 second"
     fi

     # len1 becomes len2
     len1=$len2;
fi

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.