Search code examples
linuxbashrustterminalsystemd

How to print the timestamp on the terminal when user press Enter


First of all I am new to Linux and Rust.

What I am trying to achieve is to print the timestamp to the terminal whenever a user execute a command in the terminal.

I have written a Rust program which will print the current timestamp on the right extreme of the terminal. I was planning to execute this program as systemd service in the background using a bash script. What this bash script does is inside an infinite loop, check the key press and if it is Enter, then execute the rust program. Before I execute the real Rust program, I just tried to echo a string. When I was running the service, I noticed that when I press Enter, echo runs many times before it stops. I also tried to execute the Rust program instead of the echo, but it didn't work the way I imagined. So my solution is wrong smewhere.

My question is, is my approach for this correct? I don't know whether running a background process with an infinite loop is good. This idea I took from the below video.

Creating systemd Service Files

This project is for educational purpose. I was inspired by the Powerline project and wanted to understand how it works and do something similar in small scale using Rust.

Could you guys let me know whether this approach is correct or point me to the right direction. Thanks

main.rs

extern crate termion;
extern crate chrono;


use std::time::SystemTime;
use std::io::stdout;
use chrono::{DateTime, Utc};
use termion::{color, terminal_size, cursor, raw::IntoRawMode, cursor::DetectCursorPos};

fn main() {
    let mut stdout = stdout().into_raw_mode().unwrap();
    let cursor_pos = stdout.cursor_pos().expect("Could not get the cursor position");
    let time_now = SystemTime::now();
    let datetime = DateTime::<Utc>::from(time_now);
    let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
    let size = terminal_size().expect("Could not get the terminal size");
    let x = size.0 - (timestamp_str.len() - 1) as u16;
    let y = cursor_pos.1;
    print!("{}{red}{}{reset}",
        cursor::Goto(x, y),
        timestamp_str,
        red = color::Fg(color::Red),
        reset = color::Fg(color::Reset));
}

bash script

#!/bin/bash

IFS=

while true
do
    read -n 1 key
    if [ "$key" = "" ]; then
        echo "This was really Enter, not space, tab or something else"
    fi
done

logs from the service

Apr 04 20:49:42 archlinux systemd[1]: Started cmd-timestamp.service.
Apr 04 20:49:42 archlinux cmd-timestamp.sh[410670]: thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code:>
Apr 04 20:49:42 archlinux cmd-timestamp.sh[410670]: note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Apr 04 20:49:42 archlinux systemd[1]: cmd-timestamp.service: Main process exited, code=exited, status=101/n/a
Apr 04 20:49:42 archlinux systemd[1]: cmd-timestamp.service: Failed with result 'exit-code'.

Solution

  • Taking the comment to the answer section:

    The easiest way to accomplish what I think you want is to hook your program into bash via the PROMPT_COMMAND.

    In your ~/.bashrc (or ~/.bash_login) set:

    export PROMPT_COMMAND=/path/to/rust/executable