Advanced Bash-Scripting Guide:
Prev | Chapter 16. External Filters, Programs and Commands | Next

# 16.3. Time / Date Commands

Time/date and timing

date

  Simply invoked, date prints the date and time to `stdout`. Where this command gets interesting is in its formatting and parsing options.

  Example 16-10. Using date

      #!/bin/bash
      # Exercising the 'date' command

      echo "The number of days since the year's beginning is `date +%j`."
      # Needs a leading '+' to invoke formatting.
      # %j gives day of year.

      echo "The number of seconds elapsed since 01/01/1970 is `date +%s`."
      #  %s yields number of seconds since "UNIX epoch" began,
      #+ but how is this useful?

      prefix=temp
      suffix=$(date +%s)  # The "+%s" option to 'date' is GNU-specific.
      filename=$prefix.$suffix
      echo "Temporary filename = $filename"
      #  It's great for creating "unique and random" temp filenames,
      #+ even better than using $$.

      # Read the 'date' man page for more formatting options.

      exit 0

  The `-u` option gives the UTC (Universal Coordinated Time).

      bash$ date
      Fri Mar 29 21:07:39 MST 2002

      bash$ date -u
      Sat Mar 30 04:07:42 UTC 2002

  This option facilitates calculating the time between different dates.

  Example 16-11. Date calculations

      #!/bin/bash
      # date-calc.sh
      # Author: Nathan Coulter
      # Used in ABS Guide with permission (thanks!).

      MPHR=60    # Minutes per hour.
      HPD=24     # Hours per day.

      diff () {
              printf '%s' $(( $(date -u -d"$TARGET" +%s) -
                              $(date -u -d"$CURRENT" +%s)))
      #                       %d = day of month.
      }

      CURRENT=$(date -u -d '2007-09-01 17:30:24' '+%F %T.%N %Z')
      TARGET=$(date -u -d'2007-12-25 12:30:00' '+%F %T.%N %Z')
      # %F = full date, %T = %H:%M:%S, %N = nanoseconds, %Z = time zone.

      printf '\nIn 2007, %s ' \
             "$(date -d"$CURRENT +
              $(( $(diff) /$MPHR /$MPHR /$HPD / 2 )) days" '+%d %B')"
      #       %B = name of month                ^ halfway
      printf 'was halfway between %s ' "$(date -d"$CURRENT" '+%d %B')"
      printf 'and %s\n' "$(date -d"$TARGET" '+%d %B')"

      printf '\nOn %s at %s, there were\n' \
              $(date -u -d"$CURRENT" +%F) $(date -u -d"$CURRENT" +%T)
      DAYS=$(( $(diff) / $MPHR / $MPHR / $HPD ))
      CURRENT=$(date -d"$CURRENT +$DAYS days" '+%F %T.%N %Z')
      HOURS=$(( $(diff) / $MPHR / $MPHR ))
      CURRENT=$(date -d"$CURRENT +$HOURS hours" '+%F %T.%N %Z')
      MINUTES=$(( $(diff) / $MPHR ))
      CURRENT=$(date -d"$CURRENT +$MINUTES minutes" '+%F %T.%N %Z')
      printf '%s days, %s hours, ' "$DAYS" "$HOURS"
      printf '%s minutes, and %s seconds ' "$MINUTES" "$(diff)"
      printf 'until Christmas Dinner!\n\n'

      #  Exercise:
      #  --------
      #  Rewrite the diff () function to accept passed parameters,
      #+ rather than using global variables.

  The date command has quite a number of output options. For example `%N` gives the nanosecond portion of the current time. One interesting use for this is to generate random integers.

      date +%N | sed -e 's/000$//' -e 's/^0//'
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      #  Strip off leading and trailing zeroes, if present.
      #  Length of generated integer depends on
      #+ how many zeroes stripped off.

      # 115281032
      # 63408725
      # 394504284

  There are many more options (try man date).

      date +%j
      # Echoes day of the year (days elapsed since January 1).

      date +%k%M
      # Echoes hour and minute in 24-hour format, as a single digit string.

      # The 'TZ' parameter permits overriding the default time zone.
      date                 # Mon Mar 28 21:42:16 MST 2005
      TZ=EST date          # Mon Mar 28 23:42:16 EST 2005
      # Thanks, Frank Kannemann and Pete Sjoberg, for the tip.

      SixDaysAgo=$(date --date='6 days ago')
      OneMonthAgo=$(date --date='1 month ago')  # Four weeks back (not a month!)
      OneYearAgo=$(date --date='1 year ago')

  See also Example 3-4 and Example A-43.

zdump

  Time zone dump: echoes the time in a specified time zone.

      bash$ zdump EST
      EST  Tue Sep 18 22:09:22 2001 EST

time

  Outputs verbose timing statistics for executing a command.

  `time ls -l /` gives something like this:

      real    0m0.067s
       user    0m0.004s
       sys     0m0.005s

  See also the very similar times command in the previous section.

  | As of version 2.0 of Bash, time became a shell reserved word, with slightly altered behavior in a pipeline.

touch

  Utility for updating access/modification times of a file to current system time or other specified time, but also useful for creating a new file. The command `touch zzz` will create a new file of zero length, named `zzz`, assuming that `zzz` did not previously exist. Time-stamping empty files in this way is useful for storing date information, for example in keeping track of modification times on a project.

  | The touch command is equivalent to `: >> newfile` or `>> newfile` (for ordinary files).

  | Before doing a cp -u (copy/update), use touch to update the time stamp of files you don't wish overwritten. As an example, if the directory `/home/bozo/tax_audit` contains the files `spreadsheet-051606.data`, `spreadsheet-051706.data`, and `spreadsheet-051806.data`, then doing a touch spreadsheet*.data will protect these files from being overwritten by files with the same names during a cp -u /home/bozo/financial_info/spreadsheet*data /home/bozo/tax_audit.

at

  The at job control command executes a given set of commands at a specified time. Superficially, it resembles cron, however, at is chiefly useful for one-time execution of a command set.

  `at 2pm January 15` prompts for a set of commands to execute at that time. These commands should be shell-script compatible, since, for all practical purposes, the user is typing in an executable shell script a line at a time. Input terminates with a Ctl-D.

  Using either the `-f` option or input redirection (<), at reads a command list from a file. This file is an executable shell script, though it should, of course, be non-interactive. Particularly clever is including the run-parts command in the file to execute a different set of scripts.

      bash$ at 2:30 am Friday < at-jobs.list
      job 2 at 2000-10-27 02:30

batch

  The batch job control command is similar to at, but it runs a command list when the system load drops below `.8`. Like at, it can read commands from a file with the `-f` option.

cal

  Prints a neatly formatted monthly calendar to `stdout`. Will do current year or a large range of past and future years.

sleep

  This is the shell equivalent of a wait loop. It pauses for a specified number of seconds, doing nothing. It can be useful for timing or in processes running in the background, checking for a specific event every so often (polling), as in Example 32-6.

      sleep 3     # Pauses 3 seconds.

  | The sleep command defaults to seconds, but minute, hours, or days may also be specified.
      sleep 3 h   # Pauses 3 hours!

  | The watch command may be a better choice than sleep for running commands at timed intervals.

usleep

  Microsleep (the u may be read as the Greek mu, or micro- prefix). This is the same as sleep, above, but "sleeps" in microsecond intervals. It can be used for fine-grained timing, or for polling an ongoing process at very frequent intervals.

      usleep 30     # Pauses 30 microseconds.

  This command is part of the Red Hat initscripts / rc-scripts package.

  | The usleep command does not provide particularly accurate timing, and is therefore unsuitable for critical timing loops.

hwclock, clock

  The hwclock command accesses or adjusts the machine's hardware clock. Some options require root privileges. The `/etc/rc.d/rc.sysinit` startup file uses hwclock to set the system time from the hardware clock at bootup.

  The clock command is a synonym for hwclock.

Prev | Home | Next
Complex Commands | Up | Text Processing Commands