Reputation: 1109
The command date +'%a %b %e %Y%n%I:%M %p'
displays the date and time in the format:
Thu Sep 22 2016
08:02 PM
I was trying to think of a way to have the date and time update as the time changes. The only thing I can think of would be to do something like this:
#!/bin/bash
while true
do
date +'%a %b %e %Y%n%I:%M %p'
done | awk '!seen[$0]++'
This produces output like:
Thu Sep 22 2016
08:02 PM
08:03 PM
08:04 PM
Is there a way to display the time change by overwriting the old time so that 08:02
changes to 08:03
on the same line, essentially making the date and time display like a regular digital clock?
Upvotes: 3
Views: 10279
Reputation: 11
I wanted a 24h clock centered vertically and horizontally with the date on a second line that re-centered if the window size or text size changed
this is in /usr/bin/clock as in my opinion it is starting to get a bit large for a addition to bashrc but its only one function so no reason you couldn't put the clock() function in bashrc
output(centered to screen):
19:28:13
Tuesday April 9 2024
code:
#!/usr/bin/env bash
clock() {
local lastDim=("0" "0")
local lines=()
display_center() {
local termDim=("$(tput lines)" "$(tput cols)")
if [ "$((${termDim[0]} + ${termDim[1]}))" != "$((${lastDim[0]} + ${lastDim[1]}))" ]; then
clear
lastDim=(${termDim[@]})
fi
tput cup $((${termDim[0]} / 2 - $((${#lines[@]} / 2))))
for ((i = 0; i < ${#lines[@]}; i++)); do
printf '%*s\n' $(((${#lines[i]} + ${termDim[1]}) / 2)) "${lines[i]}"
tput el
done
}
local LOOPDELAY
trap 'tput cnorm; trap - INT ERR' ERR
trap 'tput cnorm; trap - INT ERR; return' INT
tput civis
while :; do
lines=("$(date +%0H:%0M:%0S)")
lines+=("$(date +"%A %B %-d %Y")")
display_center
LOOPDELAY=$(expr 1200000000 - $(command -p date '+%N'))
LOOPDELAY=$(expr ${LOOPDELAY} / 100000000)
sleep "$(expr ${LOOPDELAY} / 10).$(expr ${LOOPDELAY} % 10)"
done
}
clock
Upvotes: 0
Reputation: 390
I also wanted to have a simple, single-line live digital clock displayed and automatically updated in my bash(1)
shell when I need it. Here's the definition of the clock()
function I implemented, which I use in my ~/.bashrc
file:
if command -v 'tput' > /dev/null 2>&1; then
function clock() {
trap 'tput cnorm; trap - INT ERR' ERR
trap 'tput cnorm; trap - INT ERR; return' INT
tput civis
while :; do
echo -e 'cr\nel' | tput -S
echo -n "$(date) "
sleep 1
done
}
fi
This function uses the tput(1)
utility and the terminfo(5)
terminal capability database to hide the cursor (civis
or cursor_invisible
capability), to move the cursor to the start of the line (cr
or carriage_return
capability), and to clear the line to its end (el
or clr_eol
capability) before updating its contents. It also uses tput(1)
to make the cursor visible again (cnorm
or cursor_normal
capability) before returning back to the shell.
As visible in the shell code above, this function uses a bit of the shell's trap
trickery to achieve the desired behavior, but it all works fine with no issues or side effects. You can verify that no temporary trap
leftovers are present by executing trap -p
before and after this function is executed and interrupted by pressing Ctrl + C. This function depends on the tput(1)
utility, which is usually part of the ncurses(3X)
package that isn't always installed, so the function doesn't get defined at all in case the tput(1)
utility isn't present.
Of course, you can also define a date
alias to customize the default output format for the date
command, or you can modify the clock()
function to additionally specify the format string for the date
command. For example, here's the definition of an alias that I use in my ~/.bashrc
file:
alias date="date '+%A %B %-d, %Y %H:%M:%S %Z (%:z)'"
Here's another version of the clock()
function that avoids the use of a couple of bashisms present in the original version of the function, and instead uses more portable constructs as suggested in the comments below:
if command -v 'tput' > /dev/null 2>&1; then
function clock() {
trap 'tput cnorm; trap - INT ERR' ERR
trap 'tput cnorm; trap - INT ERR; return' INT
tput civis
while :; do
printf 'cr\nel\n' | tput -S
printf '%s' "$(date) "
sleep 1
done
}
fi
However, as also discussed in the comments below, this more portable version of the function most probably still requires bash(1)
because the required ERR
trap seems not to be available in the POSIX sh(1P)
.
Below are the improved versions of the clock()
function, which no longer suffer from the occasional flickering of the displayed digital clock, and eliminate the occasional skipping of an elapsed second.
The flickering was pronounced especially on slower systems, such as ARM-based devices, loaded with other resource-hungry processes. It was caused by clearing the line to its end (el
or clr_eol
capability) before updating its contents, which is actually completely redundant, because all we need is to clear the line to its end after updating its contents, to cover the cases in which the updated contents has fewer characters than the replaced contents.
Simply overwriting the contents, which in most cases actually isn't much different than the replaced contents, avoids the flickering, and the subsequent execution of tput el
handles the clearing of the rest of the line.
These improved versions also eliminate the occasional (actually, pretty much periodic) skipping of displaying an elapsed second while the digital clock is updated. Previously, using the fixed one-second update delay interval caused displaying an elapsed second to be skipped periodically because the delays created by executing various commands added up over time, causing the overall update delay to occasionally become longer than one second.
In addition to eliminating the skipped seconds, these improved versions also produce much more fluent updating of the digital clock, by performing the updates always around ss.200000000
, on each elapsed second, where 200000000
is the nanosecond portion of the actual time.
Here's the improved fluent, flicker-free and skip-free version of the clock()
function that uses some bashisms:
if command -v 'tput' > /dev/null 2>&1; then
function clock() {
local LOOPDELAY
trap 'tput cnorm; trap - INT ERR' ERR
trap 'tput cnorm; trap - INT ERR; return' INT
tput civis
while :; do
tput cr
echo -n "$(date) "
tput el
LOOPDELAY=$(expr 1200000000 - $(command -p date '+%N'))
LOOPDELAY=$(expr ${LOOPDELAY} / 100000000)
sleep "$(expr ${LOOPDELAY} / 10).$(expr ${LOOPDELAY} % 10)"
done
}
fi
Here's also the improved fluent, flicker-free and skip-free version of the clock()
function that uses fewer bashisms:
if command -v 'tput' > /dev/null 2>&1; then
function clock() {
local LOOPDELAY
trap 'tput cnorm; trap - INT ERR' ERR
trap 'tput cnorm; trap - INT ERR; return' INT
tput civis
while :; do
tput cr
printf '%s' "$(date) "
tput el
LOOPDELAY=$(expr 1200000000 - $(command -p date '+%N'))
LOOPDELAY=$(expr ${LOOPDELAY} / 100000000)
sleep "$(expr ${LOOPDELAY} / 10).$(expr ${LOOPDELAY} % 10)"
done
}
fi
However, as discussed in the comments below, this more portable version of the function most probably still requires bash(1)
because the required ERR
trap seems not to be available in the POSIX sh(1P)
.
Obviously, the improved versions above rely on the sleep(1)
utility being capable of accepting fractional delay values, which most of its implementations and versions seem to support properly.
All versions of the clock()
function have been updated to trap
properly SIGINT
as well, and to clear this additional trap
handler, which was required to handle Ctrl + C keypresses properly in all cases.
Upvotes: 1
Reputation: 23850
You can use tput
to move the cursor around:
date +'%a %b %e %Y%n%I:%M %p'
while sleep 1
do
tput cuu 2
date +'%a %b %e %Y%n%I:%M %p'
done
Upvotes: 2
Reputation: 295363
To update the first line only when the day changes, and the second line every second:
while :; do
# store start date in variable
printf -v start_day '%(%a %b %e %Y)T'
day=$start_day
clear
echo "$day"
while sleep 1 && [[ $start_day = $day ]]; do
printf -v day '%(%a %b %e %Y)T'
printf '\r%(%I:%M %p)T'
done
done
Using printf %()T
restricts compatibility to recent bash 4.x, but substantially improves performance (avoiding the need to start an external program every time we want to update the time).
Upvotes: 3