0

I'm currently writing a bash script to do tasks automatically. In my script I want it to display progress message when it is doing a task.

For example:

user@ubuntu:~$ Configure something

->

Configure something .

->

Configure something ..

->

Configure something ...

->

Configure something ... done

All the progress message should appear in the same line. Below is my workaround so far:

echo -n "Configure something "
exec "configure something 2>&1 /dev/null"
//pseudo code for progress message
echo -n "." and sleep 1 if the previous exec of configure something not done
echo " done" if exec of the command finished successfully
echo " failed" otherwise

Will exec wait for the command to finish and then continue with the script lines later? If so, then how can I echo message at the same time the exec of configure something is taking place? How do I know when exec finishes the previous command and return true? use $? ?

2 Answers 2

1

Just to put the editorial hat on, what if something goes wrong? How are you, or a user of your script going to know what went wrong? This is probably not the answer you're looking for but having your script just execute each build step individually may turn out to be better overall, especially for troubleshooting. Why not define a function to validate your build steps:

function validateCmd()
{
  CODE=$1
  COMMAND=$2
  MODULE=$3

  if [ ${CODE} -ne 0 ]; then
    echo "ERROR Executing Command: \"${COMMAND}\" in Module: ${MODULE}"
    echo "Exiting."
    exit 1;
  fi
}    

./configure
validateCmd $? "./configure" "Configuration of something"

Anyways, yes as you probably noticed above, use $? to determine what the result of the last command was. For example:

rm -rf ${TMP_DIR}

if [ $? -ne 0 ]; then
  echo "ERROR Removing directory: ${TMP_DIR}"
  exit 1;
fi

To answer your first question, you can use:

echo -ne "\b"

To delete a character on the same line. So to count to ten on one line, you can do something like:

for i in $(seq -w 1 10); do
  echo -en "\b\b${i}"
  sleep .25
done
echo

The trick with that is you'll have to know how much to delete, but I'm sure you can figure that out.

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

2 Comments

Thank you. You are right, I shouldn't direct all the stdout and stderr to null, but instead to some log file.
cmd; if [ $? -ne 0 ]; then foo is better and more idiomatically written if ! cmd; then foo
1

You cannot call exec like that; exec never returns, and the lines after an exec will not execute. The standard way to print progress updates on a single line is to simply use \r instead of \n at the end of each line. For example:

#!/bin/bash

i=0
sleep 5 &   # Start some command
pid=$!      # Save the pid of the command
while sleep 1; do    # Produce progress reports
    printf '\rcontinuing in %d seconds...' $(( 5 - ++i ))
    test $i -eq 5 && break
done
if wait $pid; then echo done; else echo failed; fi

Here's another example:

#!/bin/bash

execute() {
    eval "$@" &  # Execute the command
    pid=$!

    # Invoke a shell to print status.  If you just invoke
    # the while loop directly, killing it will generate a
    # notification.  By trapping SIGTERM, we suppress the notice.
    sh -c 'trap exit SIGTERM
    while printf "\r%3d:%s..." $((++i)) "$*"; do sleep 1
    done' 0 "$@" &
    last_report=$!
    if wait $pid; then echo done; else echo failed; fi
    kill $last_report
}
execute sleep 3
execute sleep 2 \| false   # Execute a command that will fail
execute sleep 1

1 Comment

This method is superior to the method I've used in the past. Thanks!

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.