ets
is a command output timestamper — it prefixes each line of a command's output with a timestamp.
The purpose of ets
is similar to that of moreutils ts(1)
, but ets
differentiates itself from similar offerings by running commands directly within ptys, hence solving thorny issues like pipe buffering and commands disabling color and interactive features when detecting a pipe as output. (ets
does provide a reading-from-stdin mode if you insist.) ets
also recognizes carriage return as a line seperator, so it doesn't choke if your command prints a progress bar. A more detailed comparison of ets
and ts
can be found below.
ets
currently supports macOS, Linux, and various other *ix variants.
Run a command with ets
:
$ ets ping localhost
[2020-06-16 17:13:03] PING localhost (127.0.0.1): 56 data bytes
[2020-06-16 17:13:03] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
[2020-06-16 17:13:04] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
[2020-06-16 17:13:05] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
...
Run a shell command:
$ ets 'ping localhost | grep icmp'
[2020-06-16 17:13:03] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
[2020-06-16 17:13:04] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
[2020-06-16 17:13:05] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
...
Pipe command output into stdin:
$ ping localhost | grep icmp | ets
[2020-06-16 17:13:03] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
[2020-06-16 17:13:04] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
[2020-06-16 17:13:05] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
...
Show elapsed time:
$ ets -s ping -i5 localhost
[00:00:00] PING localhost (127.0.0.1): 56 data bytes
[00:00:00] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.039 ms
[00:00:05] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.031 ms
[00:00:10] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.030 ms
[00:00:15] 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.045 ms
...
Show incremental time (since last timestamp):
$ ets -i ping -i5 localhost
[00:00:00] PING localhost (127.0.0.1): 56 data bytes
[00:00:00] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.043 ms
[00:00:05] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.047 ms
[00:00:05] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.116 ms
[00:00:05] 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.071 ms
...
Use a different timestamp format:
$ ets -f '%b %d %T|' ping localhost
Jun 16 17:13:03| PING localhost (127.0.0.1): 56 data bytes
Jun 16 17:13:03| 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
Jun 16 17:13:04| 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
Jun 16 17:13:05| 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
...
Millisecond precision (microsecond available too):
$ ets -s -f '[%T.%L]' ping -i 0.1 localhost
[00:00:00.004] PING localhost (127.0.0.1): 56 data bytes
[00:00:00.004] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.032 ms
[00:00:00.108] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.038 ms
[00:00:00.209] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.051 ms
[00:00:00.311] 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.049 ms
...
Use a different timezone:
$ ets ping localhost # UTC
[2020-06-16 09:13:03] PING localhost (127.0.0.1): 56 data bytes
[2020-06-16 09:13:03] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
[2020-06-16 09:13:04] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
[2020-06-16 09:13:05] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
$ ets -z America/Los_Angeles -f '[%F %T%z]' ping localhost
[2020-06-16 02:13:03-0700] PING localhost (127.0.0.1): 56 data bytes
[2020-06-16 02:13:03-0700] 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
[2020-06-16 02:13:04-0700] 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.077 ms
[2020-06-16 02:13:05-0700] 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms
Color the timestamps:
$ ets -c ping localhost
...
-
On macOS you can install ets with Homebrew:
brew tap zmwangx/ets https://github.com/zmwangx/ets brew install zmwangx/ets/ets
-
On macOS and Linux you get download a prebuilt tarball/package from the release page.
-
On Arch Linux you can install the ets-bin binary package from AUR:
pacman -S ets-bin # or yay -S ets-bin
-
On a supported platform, if you have the Go toolchain installed, you may install with
go get github.com/zmwangx/ets
ETS(1) BSD General Commands Manual ETS(1)
NAME
ets -- command output timestamper
SYNOPSIS
ets [-s | -i] [-f format] [-u | -z timezone] command [arg ...]
ets [options] shell_command
ets [options]
DESCRIPTION
ets prefixes each line of a command's output with a timestamp. Lines are
delimited by CR, LF, or CRLF.
The three forms in SYNOPSIS correspond to three command execution modes:
o If given a single command without whitespace(s), or a command and its
arguments, execute the command with exec in a pty;
o If given a single command with whitespace(s), the command is treated as
a shell command and executed as `SHELL -c shell_command', where SHELL
is the current user's login shell, or sh if login shell cannot be
determined;
o If given no command, output is read from stdin, and the user is respon-
sible for piping in a command's output.
There are three mutually exclusive timestamp modes:
o The default is absolute time mode, where timestamps from the wall clock
are shown;
o -s, --elapsed turns on elapsed time mode, where every timestamp is the
time elapsed from the start of the command (using a monotonic clock);
o -i, --incremental turns on incremental time mode, where every timestamp
is the time elapsed since the last timestamp (using a monotonic clock).
The default format of the prefixed timestamps depends on the timestamp
mode active. Users may supply a custom format string with the -f,
--format option.
The timezone for absolute timestamps can be controlled via the -u, --utc
and -z, --timezone options. Local time is used by default.
The full list of options:
-s, --elapsed
Run in elapsed time mode.
-i, --incremental
Run in incremental time mode.
-f, --format format
Use custom strftime(3)-style format string format for prefixed
timestamps.
The default is ``[%Y-%m-%d %H:%M:%S]'' for absolute time mode
and ``[%H:%M:%S]'' for elapsed and incremental time modes.
See FORMATTING DIRECTIVES for details.
-u, --utc
Use UTC for absolute timestamps instead of local time.
This option is mutually exclusive with --z, --timezone.
-z, --timezone timezone
Use timezone for absolute timestamps instead of local time.
timezone is an IANA time zone name, e.g.
``America/Los_Angeles''.
This option is mutually exclusive with -u, --utc.
-c, --color
Print timestamps in color.
FORMATTING DIRECTIVES
Formatting directives largely match strftime(3)'s directives on FreeBSD
and macOS, with the following differences:
o Additional directives %f for microsecond and %L for millisecond are
supported.
o POSIX locale extensions %E* and %O* are not supported;
o glibc extensions %-*, %_*, and %0* are not supported;
o Directives %G, %g, and %+ are not supported.
Below is the full list of supported directives:
%A is replaced by national representation of the full weekday name.
%a is replaced by national representation of the abbreviated weekday
name.
%B is replaced by national representation of the full month name.
%b is replaced by national representation of the abbreviated month
name.
%C is replaced by (year / 100) as decimal number; single digits are
preceded by a zero.
%c is replaced by national representation of time and date.
%D is equivalent to ``%m/%d/%y''.
%d is replaced by the day of the month as a decimal number (01-31).
%e is replaced by the day of the month as a decimal number (1-31);
single digits are preceded by a blank.
%F is equivalent to ``%Y-%m-%d''.
%f is replaced by the microsecond as a decimal number (000000-999999).
%H is replaced by the hour (24-hour clock) as a decimal number
(00-23).
%h the same as %b.
%I is replaced by the hour (12-hour clock) as a decimal number
(01-12).
%j is replaced by the day of the year as a decimal number (001-366).
%k is replaced by the hour (24-hour clock) as a decimal number (0-23);
single digits are preceded by a blank.
%L is replaced by the millisecond as a decimal number (000-999).
%l is replaced by the hour (12-hour clock) as a decimal number (1-12);
single digits are preceded by a blank.
%M is replaced by the minute as a decimal number (00-59).
%m is replaced by the month as a decimal number (01-12).
%n is replaced by a newline.
%p is replaced by national representation of either "ante meridiem"
(a.m.) or "post meridiem" (p.m.) as appropriate.
%R is equivalent to ``%H:%M''.
%r is equivalent to ``%I:%M:%S %p''.
%S is replaced by the second as a decimal number (00-60).
%s is replaced by the number of seconds since the Epoch, UTC (see
mktime(3)).
%T is equivalent to ``%H:%M:%S''.
%t is replaced by a tab.
%U is replaced by the week number of the year (Sunday as the first day
of the week) as a decimal number (00-53).
%u is replaced by the weekday (Monday as the first day of the week) as
a decimal number (1-7).
%V is replaced by the week number of the year (Monday as the first day
of the week) as a decimal number (01-53). If the week containing
January 1 has four or more days in the new year, then it is week 1;
otherwise it is the last week of the previous year, and the next
week is week 1.
%v is equivalent to ``%e-%b-%Y''.
%W is replaced by the week number of the year (Monday as the first day
of the week) as a decimal number (00-53).
%w is replaced by the weekday (Sunday as the first day of the week) as
a decimal number (0-6).
%X is replaced by national representation of the time.
%x is replaced by national representation of the date.
%Y is replaced by the year with century as a decimal number.
%y is replaced by the year without century as a decimal number
(00-99).
%Z is replaced by the time zone name.
%z is replaced by the time zone offset from UTC; a leading plus sign
stands for east of UTC, a minus sign for west of UTC, hours and
minutes follow with two digits each and no delimiter between them
(common form for RFC 822 date headers).
%% is replaced by `%'.
SEE ALSO
ts(1), strftime(3)
HISTORY
The name ets comes from ``enhanced ts'', referring to moreutils ts(1).
AUTHORS
Zhiming Wang <[email protected]>
December 24, 2024
Advantages:
- Runs commands in ptys, making ets mostly transparent and avoiding pipe-related issues like buffering and lost coloring and interactivity.
- Recognizes carriage return as line separator, does not choke on progress bars.
- Has better operating defaults (uses monotonic clock where appropriate) and better formatting defaults (subjective).
- Supports alternative time zones.
- Is written in Go, not Perl, so you install a single executable, not script plus modules.
- Has an executable name that doesn't conflict with other known packages. moreutils as a whole is a conflicting hell, and ts alone conflicts with at least task-spooler.
Disadvantages:
- Needs an additional
-f
for format string, because ets reserves positional arguments for its core competency. Hopefully offset by better default. - Does not support the
-r
mode of ts. It's a largely unrelated mode of operation and I couldn't even getts -r
to work anywhere, maybe because optional dependencies aren't satisfied, or maybe I misunderstood the feature altogether. Anyway, not interested. - Supports fewer formatting directives. Let me know if this is actually an issue, it could be fixable.
Copyright © 2020 Zhiming Wang [email protected]
The project is distributed under the MIT license.
Special thanks to DinosoftLab on None Project for the hourglass icon used in the logo, and termtosvg for the animated terminal recording.