7

I'm running a shell script in Linux Mint that calls some processes taking few minutes. For each process I want to echo a message like this:

echo "Cleaning temporary files... X seconds."
myprocess

where X is the current elapsed time and I would like it to change every second, but not printing a new line.

Is there a good way to do that? I only found ways to print the total time in the end, but not the elapsed time while running the process.

rafa
  • 795
  • 1
  • 8
  • 25

4 Answers4

10

Use this at the beginning of your script, this creates a subprocess which runs in background and keeps on updating the status.

file=$(mktemp)
progress() {
  pc=0;
  while [ -e $file ]
    do
      echo -ne "$pc sec\033[0K\r"
      sleep 1
      ((pc++))
    done
}
progress &
#Do all the necessary staff

#now when everything is done
rm -f $file
abasu
  • 2,454
  • 19
  • 22
  • Nice solution to put the output generator into the background, and keep working in foreground! – ypnos Jun 03 '13 at 12:39
  • 1
    but everything will be screwed if any output of the rest of the script shows up on the screen :) in my opinion, this requires a fullproof `dialog` based script to handle all scenarios. but that seemed a bit of work – abasu Jun 03 '13 at 12:43
  • maybe if I have other outputs it might be tricky to handle both, but fortunately that is not my case, this works very well, thank you abasu :) – rafa Jun 03 '13 at 13:05
  • 1
    welcome, actually, that `rm -f $file` will be at the end of the script, i updated the answer :) – abasu Jun 03 '13 at 13:15
1

You'll have to run the process in the background with &, otherwise the rest of the script will wait until it finishes. Use backspaces to overwrite your current line, so make sure you don't use newlines.

So, to do what you want:

myproc &
myPid=$!  # save process id
tmp=""
while true; do
    if kill -0 "$myPid"; then  # if the process accepts a signal, keep waiting
         for i in {0..${#tmp}..1}; do
             printf "%b" "\b" # print backspaces until we have cleared the previous line
         done
         tmp=$( printf "Cleaning temp files... %t seconds." )
         printf "%s" "$tmp"
     else
         break # drop out of the while loop
     fi
     sleep 1
done
Spencer Rathbun
  • 14,510
  • 6
  • 54
  • 73
  • That's already quite good. Why don't you use carriage return, \r instead? To account for varying text lengths you could print some extra whitespace. It is cleaner. Also you forgot to sleep in your loop. – ypnos Jun 03 '13 at 12:37
  • @ypnos I'm keeping my string, so I can get the length and backspace all of it, no need for extra whitespace. I believe `\r` has the same problem as `\n`, but he can test it if he's interested. Thanks for noting the missing sleep by the way. – Spencer Rathbun Jun 03 '13 at 16:42
0

You can run each command with time:

time <command>

and then use sed/awk to exctract the elapsed time.

Claudio
  • 10,614
  • 4
  • 31
  • 71
0

Here is a way to have awk print on STDERR every seconds. You should just add:

  • when myprocess is over, create a file /tmp/SOMETHING
  • have awk include a test : it exits when /tmp/SOMETHING appears

The loop part (without the termination test... so "infinite loop" until CTRL-C) is:

 ping 127.0.0.1 | awk '
     BEGIN{cmd="date +%s"; cmd|getline startup ; close (cmd) } 
     /bytes from/ { cmd | getline D ; close (cmd) ; 
                    print D-startup | "cat >&2" }'

now you just need to use "printf" and ansi escape sequence to print without a newline, have the ansi-escape go back until the beginning of the number, and flush the output (all descriptors) by invoking system:

 ping 127.0.0.1 | awk -v getback4char="$(printf '\033[4D')"  '
 BEGIN{cmd="date +%s"; cmd|getline startup ; close (cmd) ; printf "Elapsed time: ";}
 /bytes from/ { cmd | getline D ; close (cmd) ;
                printf "%4d%s" ,(D-startup) , getback4char | "cat >&2"
                system("") }'

note: this is compatible with all version of awk I know of, even ANCIENT ones (ie, not gawk/nawk only, but also the venerable awk.)

Olivier Dulac
  • 3,695
  • 16
  • 31