Forcing an ‘added’ alias to every command

Is it possible to forcibly add a timing alias (for lack of a better way to phrase it) to every command in bash?

For example, I would like to have a specific user who, whenever a command is run, it is always wrapped either with date before and after, or time.

Is this possible, and, if so, how?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You can record the time a command line is started and the time a prompt is displayed. Bash already keeps track of the starting date of each command line in its history, and you can note the time when you display the next prompt.

print_command_wall_clock_time () {
  echo Wall clock time: 
    $(($(date +%s) - $(HISTTIMEFORMAT="%s ";
                       set -o noglob;
                       set $(history 1); echo $2)))
}
PROMPT_COMMAND=print_command_wall_clock_time$'n'"$PROMPT_COMMAND"

This only gives you second resolution, and only the wall clock time. If you want better resolution, you need to use an external date command that supports the %N format for nanoseconds, and the DEBUG trap to call date before running the command to time.

call_date_before_command () {
  date_before=$(date +%s.%N)
}
print_wall_clock_time () {
  echo "Wall clock time: $(date +"%s.%N - $date_before" | bc)"
}
trap call_date_before_command DEBUG
PROMPT_COMMAND=print_wall_clock_time

Even with the DEBUG trap, I don’t think there’s a way of automatically displaying processor times for each command, or being more discriminating than prompt to prompt.


If you’re willing to use a different shell, here’s how to get a time report for every command in zsh (this doesn’t generalize to other tasks):

REPORTTIME=0

You can set REPORTTIME to any integer value, the timing information will only be displayed for commands that used more than this many seconds of processor time.

Zsh took this feature from csh where the variable is called time.

Method 2

Your options here are going to depend on your shell. In zsh there a convenient hook function called preexec() that is run right before any interactive shell commands. By creating a function with this name, you can cause things to be executed. You can also follow up with a function called precmd() which will run just before the next prompt is drawn, which will be right after your command finishes.

By creating this pair of functions, you can have whatever arbitrary commands you want run before and after whatever commands are issued at the prompt. You could use this to log shell usage, create locks, test the environment, or as in your example calculate time or resources spent while a command runs.

In this example, we will create ourselves a benchmark timestamp before running a command using preexec() then calculate the time spent executing the command using precmd() and output it before the prompt or log it away. Example:

preexec() {
   CMDSTART=$(date +%s%N)
}
precmd() {
   CMDRUNTIME=$(($(date +%s%N)-$CMDSTART))
   echo "Last command ran for $CMDRUNTIME nanoseconds."
}

Note: For this particular example, there is an even easier builtin function. All you have to do is turn on runtime reporting in ZSH and it will do this automatically.

$ export REPORTTIME=0
$ ls -d
./
ls -BF --color=auto -d  0.00s user 0.00s system 0% cpu 0.002 total

In a more practical implementation of preexec(), I use it see if the shell is running inside tmux or screen and, if so, to send information about the currently running command upstream to be displayed in the tab name.

Unfortunately in bash this little mechanism doesn’t exist. Here is one man’s attempt to replicate it. Also see Gilles’s answer for similar nifty little hack.

Method 3

The easiest way would probably to set PROMPT_COMMAND. See Bash Variables:

PROMPT_COMMAND
If set, the value is interpreted as a command to execute before the printing of each primary prompt ($PS1).

For example, to avoid overwriting any existing prompt command, you could do:

PROMPT_COMMAND="date ; ${PROMPT_COMMAND}"

Method 4

csh/tcsh has the best support for this feature (and has always had it).

  The `time` shell variable can be set to execute the time builtin  command
  after the completion of any process that takes more than a given number
  of CPU seconds.

In other words, set time=1 will print out the time consumed (system, user, elapsed) by any command that took more than 1 second of cpu time. Plain set time will enable printing out the time for all commands.

Method 5

As pointed out by wjandrea, Gilles’s answer doesn’t work,

  • partly because of cosmetic reasons, which wjandrea has fixed, and
  • partly because the DEBUG trap command gets run
    immediately before the PROMPT_COMMAND command gets run,
    so you lose the start time of the user-typed command
    whose elapsed time you are trying to measure.

Luckily, the DEBUG trap command can tell when this is happening
and can guard against it. 
So, building on Gilles’s answer
(combining it with pieces of the DNA of my very similar answer here),
we can construct

call_date_before_command () {
  if [ "$BASH_COMMAND" != "print_wall_clock_time" ]
  then
        command_flag=1
        date_before=$(date +%s.%N)
  fi
}
print_wall_clock_time () {
  if [ "$command_flag" ]
  then
        echo "Wall clock time: $(date +"%s.%N - $date_before" | bc)"
  fi
  command_flag=
}
trap call_date_before_command DEBUG
PROMPT_COMMAND=print_wall_clock_time

where call_date_before_command doesn’t do anything
if it’s being called just before print_wall_clock_time gets run,
and print_wall_clock_time reports information
only if the user has actually run a command (and not just pressed Enter).

You might want to rename call_date_before_command, command_flag
and print_wall_clock_time to something like
Twas_brillig_and_the_slithy_toves, Did_gyre_and_gimble_in_the_wabe
and All_mimsy_were_the_borogoves, to reduce the chance
that you will inadvertently redefine one of the interactively.

Method 6

This is based on print_command_wall_clock_time from Gilles‘s answer, but changed to accommodate duplicate commands, and times when you don’t actually run a new command, just press enter on the command line. It also saves the timings in an array. And I also cleaned up the method of getting info from history.

# don't put lines starting with space in the history.
HISTCONTROL=ignorespace

_last_command_time () {
    # Get the real time the last command took to run, in seconds.
    #
    # Must be set up in PROMPT_COMMAND to get the correct end time.
    #
    # Uses "history" to get the start time, so won't work properly for
    # duplicate commands if you have "HISTCONTROL=ignoredups" enabled.
    #
    # Records times by history number in global array "COMMAND_TIMES".

    local n prev

    # Get command number and start time in seconds
    read -r n prev _ <<< "$(HISTTIMEFORMAT="%s "; history 1)"

    if ! [[ ${COMMAND_TIMES[$n]} ]]; then  # If it's not already recorded
        COMMAND_TIMES+=( [$n]=$(( $(date +%s) - prev )) )
    fi

    printf 'Last command time: %sn' "${COMMAND_TIMES[$n]}"
}

PROMPT_COMMAND=_last_command_time


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x