2

I have some sort of stopwatch script that looks like:

BEGIN=$(date +%s)

while true; do
   NOW=$(date +%s)
   DIFF=$(($NOW - $BEGIN))
   MINS=$(($DIFF / 60))
   SECS=$(($DIFF % 60))
   HOURS=$(($DIFF / 3600))

   # \r  is a "carriage return" - returns cursor to start of line
   printf "\rDownload time: %02d:%02d:%02d" $HOURS $MINS $SECS
   sleep 1
done

So while some condition is true, it will keep adding 1 second per loop. I want this condition to be something along the lines:

function download()
{
    HOMEPAGE_RESPONSE=$(curl -w "\n%{http_code}" "https://example.com/")

    STATUS_CODE=$(echo "$HOMEPAGE_RESPONSE" | sed -n '$p')

    HTML=$(echo "$HOMEPAGE_RESPONSE" | sed '$d')
}

download

# Whenever the STATUS_CODE is 200, exit the stopwatch script
# Can be any other condition that stops the loop when cURL has finished
while (( $STATUS_CODE != 200 )); do
   NOW=$(date +%s)
   DIFF=$(($NOW - $BEGIN))
   MINS=$(($DIFF / 60))
   SECS=$(($DIFF % 60))
   HOURS=$(($DIFF / 3600))

   # \r  is a "carriage return" - returns cursor to start of line
   printf "\rDownload time: %02d:%02d:%02d" $HOURS $MINS $SECS
   sleep 1
done

The idea is to initiate the cURL download and at the same time the download starts, execute the stopwatch script. This will eventually start counting seconds and printing the stopwatch until the download is complete. I am also aware of the until command which I found in this post. Example:

until $(curl --output /dev/null --silent --head --fail http://myhost:myport); do
    printf '.'
    sleep 5
done

I don't know how to apply until since my cURL is stored in a variable within the function called download() and I want to be able to use the STATUS_CODE and the HTML content separately.

Can anyone tell me how I can do so?


UPDATE

Given @Inian 's answer this is what I have so far:

function download()
{
    homepage_response=$(curl -s -w "\n%{http_code}" "https://example.com/")
    status_code=$(echo "$homepage_response" | sed -n '$p')
    html=$(echo "$homepage_response" | sed '$d')
    printf '%s' "${status_code}"
}

# Calling the function $(download)
until [[ "$(download)" == "200" ]]; do
    printf '.\n'
    sleep 1
done

echo $status_code

According to my understanding, this should execute the function download() and print . each on a different line until cURL returns the status_code of 200.

This, however, initiates cURL but neither it prints . nor does it echo the status_code which should be equivalent to 200.

I cannot guess why.


ALTERED ANSWER

According to @chepner 's answer I came up with:

download()
{
    homepage_response=$(curl -s -w "\n%{http_code}" "https://example.com/")
    status_code=$(echo "$homepage_response" | sed -n '$p')
    html=$(echo "$homepage_response" | sed '$d')
    # printf '%s' "${status_code}"
}

start_stopwatch () {
    BEGIN=$(date +%s)
    while true; do
        NOW=$(date +%s)
        DIFF=$(($NOW - $BEGIN))
        MINS=$(($DIFF / 60))
        SECS=$(($DIFF % 60))
        HOURS=$(($DIFF / 3600))

        printf "\rDownload time: %02d:%02d:%02d" "$HOURS" "$MINS" "$SECS"
        sleep 1 & wait  # Make it interruptible
    done
}

start_stopwatch & sw_pid=$!

# # For testing purposes
# echo "$sw_pid"

# Kill background stopwatch if script EXITS beforehand
set -e
kill_sw() {
    kill "$sw_pid"
}
trap kill_sw EXIT

# Call function download()
download

printf "\n"

kill "$sw_pid"

Out of precaution I added set -e which calls the function kill_sw() whenever the script gets interrupted before kill "$sw_pid" is even executed at the end.

Ava Barbilla
  • 968
  • 2
  • 18
  • 37
  • 1
    You don't need `let` if you are using the (preferred) `$((...))` syntax. – chepner Oct 22 '17 at 15:11
  • You might want to look into using the progress meter that `curl` already supplies instead. – chepner Oct 22 '17 at 15:17
  • You are right @chepner I removed the `let` from my code – Ava Barbilla Oct 22 '17 at 16:00
  • I would like to avoid using the progress meter as I sometimes find it not entirely accurate. I simply want to implement a stopwatch of my own which executes parallel to the download and at the end shows the amount of time it has taken. @chepner I updated my question, could you guess why my last script is not working? – Ava Barbilla Oct 22 '17 at 16:50

2 Answers2

1

You just need to use the function call into the until conditional. But the function download() in its current state is not printing the output of status code. With a small modification as below.

download() {
    homepage_response=$(curl -w "\n%{http_code}" "https://example.com/")    
    status_code=$(echo "$HOMEPAGE_RESPONSE" | sed -n '$p')
    html=$(echo "$HOMEPAGE_RESPONSE" | sed '$d')
    printf '%s' "${status_code}"
}

Now with the function printing the status code, use it in the conditional as

until [[ "$(download)" -eq "$(curl --output /dev/null --silent --head --fail http://myhost:myport)" ]]; do
    sleep 1
    # Your rest of the script goes here
done

As a general coding practice,

  1. You could use the arithmetic operator ((..)) over the let construct
  2. Lower case user-defined variables to avoid conflicting them with bash system specific environment variables.
  3. Function definition without the function keyword is recommended when thinking of porting the script without any bash-isms involved.
Inian
  • 80,270
  • 14
  • 142
  • 161
1

I would run the stopwatch code in the background, and kill it once curl has completed.

start_stopwatch () {
   SECONDS=0
   while true; do
     now=$SECONDS
     hours=$((now / 3600))
     now=$((now % 3600))
     mins=$((now / 60))
     secs=$((now % 60))

     printf "\rDownload time: %02d:%02d:%02d" "$hours" "$mins" "$secs"
     sleep 1
   done
}

start_stopwatch & sw_pid=$! 
curl ...
kill "$sw_pid"
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I adjusted your code a little, else it work like a charm. It's worth mentioning that for me your `while` did not work. It simply returns a static clock like `00:00:00`. I updated my question with the `while` that worked for me. Thanks @chepner ! – Ava Barbilla Oct 22 '17 at 21:44
  • Ugh, I meant `SECONDS`, not `RANDOM`. (Still not working, but I'll try to debug in a bit.) – chepner Oct 22 '17 at 21:49
  • 1
    OK, apparently you can't localize `$SECONDS`, or it loses its special powers. Just leave it as a global variable, and make sure `start_stopwatch` runs in a subshell if you care about the value of `SECONDS` in the current shell. – chepner Oct 22 '17 at 23:36