11

I'd like to know if there's a way to get the number of seconds since the UNIX epoch in any POSIX compliant shell, without resorting to non-POSIX languages like perl, or using non-POSIX extensions like GNU awk's strftime function.

Here are some solutions I've already ruled out...

date +%s // Doesn't work on Solaris

I've seen some shell scripts suggested before, which parse the output of date then derive seconds from the formatted gregorian calendar date, but they don't seem to take details like leap seconds into account.

GNU awk has the strftime function, but this isn't available in standard awk.

I could write a small C program which calls the time function, but the binary would be specific to a particular architecture.

Is there a cross platform way to do this using only POSIX compliant tools?

I'm tempted to give up and accept a dependency on perl, which is at least widely deployed.

perl -e 'print time' // Cheating (non-POSIX), but should work on most platforms

Community
  • 1
  • 1
mattbh
  • 5,230
  • 2
  • 27
  • 27

3 Answers3

14

Here is a portable / POSIX solution:

PATH=`getconf PATH` awk 'BEGIN{srand();print srand()}'

Unlike a C compiler or Perl, awk is guaranteed to be present on a POSIX compliant environment.

How it works:

  • PATH=`getconf PATH` is making sure the POSIX version of awk is called should it not be the first one in the PATH.

  • Per the POSIX standard : srand([expr]) Set the seed value for rand to expr or use the time of day if expr is omitted. The previous seed value shall be returned.

    The first call is omitting a parameter so the seed value is set to the time of day. The second call is returning that time of day, which is the number of seconds since the epoch.

    Note that with many awk implementations but not the GNU one (gawk), this first call is not required as the function already returns the expected value in the first place.

jlliagre
  • 29,783
  • 6
  • 61
  • 72
  • OK; I stand corrected. Yes, I assumed `awk`'s `srand()` would parallel the C version. It clearly doesn't. Weird, but what you describe is what the POSIX docs for `awk` say should happen. – Jonathan Leffler Jan 30 '15 at 17:52
  • On my system (Ubuntu): the first `srand()` is necessary. – jfs Jan 30 '15 at 21:28
  • @J.F.Sebastian Thanks, answer updated to state gawk behavior. – jlliagre Jan 30 '15 at 22:16
  • @jlliagre Thanks for the great answer! Note: using `printf` rather than `print` will avoid the trailing newline. – mattbh Feb 01 '15 at 07:25
2

Just for educational purposes, a bit of a hack. But it's as POSIX as you can imagine :-)

#!/bin/sh
: > x
echo "ibase=8;$(pax -wx cpio x | cut -c 48-59)" | bc
rm x

Lets see what this prints:

$ ./x.sh; date +%s
1314820066
1314820066
Jens
  • 69,818
  • 15
  • 125
  • 179
  • `pax -wx cpio x` produces `pax: Bad Option: -wx` and then usage info – Travis Well May 16 '17 at 00:57
  • `pax -wx cpio x` produces two lines: `pax: cpio header field is too small to store file x` `0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!`. The same goes for `pax -w -x cpio x`. pax version: 1:20171021-2 on Ubuntu 18.04. – kelvin Jul 26 '19 at 17:18
-4

Should be fairly portable, but requires write access to ${.CURDIR}; perhaps it could be modified.

I didn't use /tmp for the executable since most people have /tmp mounted noexec.

Works on FreeBSD, anyway.

#!/bin/sh

# Portably gets the date since epoch

bn=`basename $0`
prog=`mktemp /tmp/${bn}.mktime.XXXXXXX` || exit 1
>${prog}.c cat <<EOF
#include <stdio.h>
#include <time.h>
int main() {printf("%ju", time(NULL)); return 0; }
EOF

cc ${prog}.c
time=`./a.out`
rm ${prog} a.out

echo Time since epoch = $time seconds
j0k
  • 22,600
  • 28
  • 79
  • 90