6

I have a script that tracks time.

When I print my output in my console I get this:

0 Days, 00:00:33

When I save it to a txt file I get this:

^[[2K
  0 Days, 00:00:33

Code:

now=$(date +%s)
diff=$(($now - $begin))
mins=$(($diff / 60))
secs=$(($diff % 60))
hours=$(($diff / 3600))
days=$(($diff / 86400))

printf "\33[2K\r%3d Days, %02d:%02d:%02d" $days $hours $mins $secs
printf "\33[2K\r%3d Days, %02d:%02d:%02d" $days $hours $mins $secs >> test.txt

Where is the ^[[2Kcoming from? I'm guessing it has something to do with the format in printf.

I read a bit about printf here: http://wiki.bash-hackers.org/commands/builtin/printf. But I didn't get any wiser....

3 Answers 3

13

You have escape control sequences in your printf command i.e. printf "\33[2K....., (\e[2K clears a line), that is necessarily a control command for the terminal, that can only be understood and acted upon by a terminal device.

When you are running the script interactively inside the terminal, the terminal is interpreting the sequence properly. While saving the command's output in a file, the sequence is being treated literally as there is nothing to interpret the sequence then.

Now if you do:

cat test.txt

you'll see that the terminal is interpreting the sequence correctly again before the output being printed.

5

The succession of lines

printf "\33[2K\r%3d Days, %02d:%02d:%02d" $days $hours $mins $secs
printf "\33[2K\r%3d Days, %02d:%02d:%02d" $days $hours $mins $secs >> test.txt

is defined to print time information on the same line of a terminal. That \33[2K clears the entire (current) line, and the \r moves the cursor to the beginning of the line. The effect is to make the time information which changes, just appear to change that part of the line.

I noticed that there is no newline (\n) in the format. The question shows line-breaks (perhaps those were added for clarity).

4

You wrote it yourself, as

\33[2K

Depending on your terminal, it will understand that as an instruction to perform some action. Since you mentioned "console", you can consult the console_codes(4) man page, where this is listed:

ECMA-48 CSI sequences

CSI (or ESC [) is followed by a sequence of parameters, at most NPAR (16), that are decimal numbers separated by semicolons. An empty or absent parameter is taken to be 0. The sequence of parameters may be preceded by a single question mark.

...

K: EL - Erase line

default: from cursor to end of line.
ESC [ 1 K: erase from start of line to cursor.
ESC [ 2 K: erase whole line.

(It helps to recall that \033 is the same as ESC).

So your escape sequence is the "erase whole line" command to this terminal.

The portable (and clearer) way to write this would be

clear_line=`tput cr; tput el`
printf "${clear_line}%3d Days, %02d:%02d:%02d" $days $hours $mins $secs

Using tput means that when writing to a terminal that doesn't support the el action, or that supports it with a different escape sequence, you'll get the appropriate output rather than unintelligible garbage.


P.S. All that arithmetic is probably unnecessary, assuming GNU date. If you never exceed 7 days, you can get it down to

date -u -d "-$begin seconds +3days" '+${clear_line}%w days, %T'

Otherwise, you still need to compute $days, but you can format the seconds with %T:

now=$(date +%s)
diff=$(($now - $begin))
days=$(($diff / 86400))
date -u -d "00:00 $diff seconds" '+${clear_line}$days days, %T'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .