如何将最后一个命令的挂墙时间放在 Bash 提示符中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1862510/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 21:27:47  来源:igfitidea点击:

How can the last command's wall time be put in the Bash prompt?

bashtimeprompt

提问by Mr Fooz

Is there a way to embed the last command's elapsed wall time in a Bashprompt? I'm hoping for something that would look like this:

有没有办法在Bash提示中嵌入最后一个命令的已用时间?我希望看起来像这样:

[last: 0s][/my/dir]$ sleep 10
[last: 10s][/my/dir]$

Background

背景

I often run long data-crunching jobs and it's useful to know how long they've taken so I can estimate how long it will take for future jobs. For very regular tasks, I go ahead and record this information rigorously using appropriate logging techniques. For less-formal tasks, I'll just prepend the command with time.

我经常运行长时间的数据处理工作,知道他们花了多长时间很有用,这样我就可以估计未来工作需要多长时间。对于非常常规的任务,我会继续使用适当的日志记录技术严格记录这些信息。对于不太正式的任务,我只会在命令前加上time.

It would be nice to automatically timeevery single interactive command and have the timing information printed in a few characters rather than 3 lines.

自动执行time每个交互式命令并将时间信息打印在几个字符而不是 3 行中会很好。

采纳答案by miku

You could utilize this zsh-borrowed hook for bash: http://www.twistedmatrix.com/users/glyph/preexec.bash.txt

您可以将这个 zsh 借来的钩子用于 bash:http: //www.twistedmatrix.com/users/glyph/preexec.bash.txt

Timing done with this hook (Mac OS X): Use Growl to monitor long-running shell commands

使用此钩子完成计时 (Mac OS X):使用 Growl 监视长时间运行的 shell 命令

回答by Ville Laurikari

This is minimal stand-alone code to achieve what you want:

这是实现您想要的最少的独立代码:

function timer_start {
  timer=${timer:-$SECONDS}
}

function timer_stop {
  timer_show=$(($SECONDS - $timer))
  unset timer
}

trap 'timer_start' DEBUG
PROMPT_COMMAND=timer_stop

PS1='[last: ${timer_show}s][\w]$ '

回答by Nicolas Thery

Using your replies and some other threads, I wrote this prompt which I want to share with you. I took a screenshot in wich you can see :

使用您的回复和其他一些线程,我写了这个提示,我想与您分享。我截了一张截图,你可以看到:

  • White : Last return code
  • Green and tick mark means success (return code was 0)
  • Red and cross mark means error (return code was >0)
  • (Green or Red) : Last command execution time in parenthesis
  • (Green or Red) : Current date time (\t)
  • (Green if not root, Red if root) : the logged username
  • (Green) : the server name
  • (Blue) : the pwd directory and the usual $
  • 白色:最后的返回码
  • 绿色和勾号表示成功(返回码为 0)
  • 红色和十字标记表示错误(返回码 >0)
  • (绿色或红色):括号中的最后命令执行时间
  • (绿色或红色):当前日期时间 (\t)
  • (如果不是 root 则为绿色,如果为 root 则为红色):记录的用户名
  • (绿色):服务器名称
  • (蓝色) : pwd 目录和通常的 $

Custom prompt

自定义提示

Here is the code to put in your ~/.bashrc file :

这是放入 ~/.bashrc 文件的代码:

function timer_now {
    date +%s%N
}

function timer_start {
    timer_start=${timer_start:-$(timer_now)}
}

function timer_stop {
    local delta_us=$((($(timer_now) - $timer_start) / 1000))
    local us=$((delta_us % 1000))
    local ms=$(((delta_us / 1000) % 1000))
    local s=$(((delta_us / 1000000) % 60))
    local m=$(((delta_us / 60000000) % 60))
    local h=$((delta_us / 3600000000))
    # Goal: always show around 3 digits of accuracy
    if ((h > 0)); then timer_show=${h}h${m}m
    elif ((m > 0)); then timer_show=${m}m${s}s
    elif ((s >= 10)); then timer_show=${s}.$((ms / 100))s
    elif ((s > 0)); then timer_show=${s}.$(printf %03d $ms)s
    elif ((ms >= 100)); then timer_show=${ms}ms
    elif ((ms > 0)); then timer_show=${ms}.$((us / 100))ms
    else timer_show=${us}us
    fi
    unset timer_start
}


set_prompt () {
    Last_Command=$? # Must come first!
    Blue='\[\e[01;34m\]'
    White='\[\e[01;37m\]'
    Red='\[\e[01;31m\]'
    Green='\[\e[01;32m\]'
    Reset='\[\e[00m\]'
    FancyX='247'
    Checkmark='243'


    # Add a bright white exit status for the last command
    PS1="$White$? "
    # If it was successful, print a green check mark. Otherwise, print
    # a red X.
    if [[ $Last_Command == 0 ]]; then
        PS1+="$Green$Checkmark "
    else
        PS1+="$Red$FancyX "
    fi

    # Add the ellapsed time and current date
    timer_stop
    PS1+="($timer_show) \t "

    # If root, just print the host in red. Otherwise, print the current user
    # and host in green.
    if [[ $EUID == 0 ]]; then
        PS1+="$Red\u$Green@\h "
    else
        PS1+="$Green\u@\h "
    fi
    # Print the working directory and prompt marker in blue, and reset
    # the text color to the default.
    PS1+="$Blue\w \$$Reset "
}

trap 'timer_start' DEBUG
PROMPT_COMMAND='set_prompt'

回答by willdye

Another very minimal approach is:

另一个非常简单的方法是:

trap 'SECONDS=0' DEBUG
export PS1='your_normal_prompt_here ($SECONDS) # '

This shows the number of seconds since the last simple command was started. The counter is not reset if you simply hit Enter without entering a command -- which can be handy when you just want to see how long the terminal has been up since you last did anything in it. It works fine for me in Red Hat and Ubuntu. It did NOT work for me under Cygwin, but I'm not sure if that's a bug or just a limitation of trying to run Bash under Windows.

这显示自上一个简单命令启动以来的秒数。如果您在没有输入命令的情况下直接按 Enter 键,则不会重置计数器 - 当您只想查看自上次在其中执行任何操作后终端已启动多长时间时,这会很方便。它在 Red Hat 和 Ubuntu 中对我来说很好用。它在 Cygwin 下对我不起作用,但我不确定这是一个错误还是只是尝试在 Windows 下运行 Bash 的限制。

One possible drawback to this approach is that you keep resetting SECONDS, but if you truly need to preserve SECONDS as the number of seconds since initial shell invocation, you can create your own variable for the PS1 counter instead of using SECONDS directly. Another possible drawback is that a large seconds value such as "999999" might be be better displayed as days+hours+minutes+seconds, but it's easy to add a simple filter such as:

这种方法的一个可能的缺点是您不断重置 SECONDS,但如果您确实需要将 SECONDS 保留为自初始 shell 调用以来的秒数,您可以为 PS1 计数器创建自己的变量,而不是直接使用 SECONDS。另一个可能的缺点是像“999999”这样的大秒值可能会更好地显示为天+小时+分钟+秒,但是添加一个简单的过滤器很容易,例如:

seconds2days() { # convert integer seconds to Ddays,HH:MM:SS
  printf "%ddays,%02d:%02d:%02d" $((((/60)/60)/24)) \
  $((((/60)/60)%24)) $(((/60)%60)) $((%60)) |
  sed 's/^1days/1day/;s/^0days,\(00:\)*//;s/^0//' ; }
trap 'SECONDS=0' DEBUG
PS1='other_prompt_stuff_here ($(seconds2days $SECONDS)) # '

This translates "999999" into "11days,13:46:39". The sed at the end changes "1days" to "1day", and trims off empty leading values such as "0days,00:". Adjust to taste.

这将“999999”翻译成“11days,13:46:39”。最后的 sed 将“1days”更改为“1day”,并修剪掉空的前导值,例如“0days,00:”。根据口味调整。

回答by philsnow

If you hadn't set up any of the other answers before you kicked off your long-running job and you just want to know how long the job took, you can do the simple

如果您在开始长期运行的工作之前没有设置任何其他答案,而您只想知道工作需要多长时间,您可以做简单的

$ HISTTIMEFORMAT="%s " history 2

and it will reply with something like

它会回复类似的东西

  654  1278611022 gvn up
  655  1278611714 HISTTIMEFORMAT="%s " history 2

and you can then just visually subtract the two timestamps (anybody know how to capture the output of the shell builtin history command?)

然后您可以直观地减去两个时间戳(有人知道如何捕获 shell 内置历史命令的输出吗?)

回答by Thomas

I took the answer from Ville Laurikari and improved it using the timecommand to show sub-second accuracy:

我从 Ville Laurikari 那里得到了答案,并使用time命令改进了它以显示亚秒级精度:

function timer_now {
  date +%s%N
}

function timer_start {
  timer_start=${timer_start:-$(timer_now)}
}

function timer_stop {
  local delta_us=$((($(timer_now) - $timer_start) / 1000))
  local us=$((delta_us % 1000))
  local ms=$(((delta_us / 1000) % 1000))
  local s=$(((delta_us / 1000000) % 60))
  local m=$(((delta_us / 60000000) % 60))
  local h=$((delta_us / 3600000000))
  # Goal: always show around 3 digits of accuracy
  if ((h > 0)); then timer_show=${h}h${m}m
  elif ((m > 0)); then timer_show=${m}m${s}s
  elif ((s >= 10)); then timer_show=${s}.$((ms / 100))s
  elif ((s > 0)); then timer_show=${s}.$(printf %03d $ms)s
  elif ((ms >= 100)); then timer_show=${ms}ms
  elif ((ms > 0)); then timer_show=${ms}.$((us / 100))ms
  else timer_show=${us}us
  fi
  unset timer_start
}

trap 'timer_start' DEBUG
PROMPT_COMMAND=timer_stop

PS1='[last: ${timer_show}][\w]$ '

Of course this requires a process to be started, so it's less efficient, but still fast enough that you wouldn't notice.

当然,这需要启动一个进程,因此效率较低,但仍然足够快,您不会注意到。

回答by PJSCopeland

I found that trap ... DEBUGwas running every time $PROMPT_COMMANDwas called, resetting the timer, and therefore always returning 0.

我发现trap ... DEBUG每次$PROMPT_COMMAND调用时都在运行,重置计时器,因此总是返回 0。

However, I found that historyrecords times, and I tapped into these to get my answer:

然而,我发现history记录时间,我利用这些来得到我的答案:

HISTTIMEFORMAT='%s '
PROMPT_COMMAND="
  START=$(history 1 | cut -f5 -d' ');
  NOW=$(date +%s);
  ELAPSED=$[NOW-START];
  $PROMPT_COMMAND"
PS1="$ELAPSED $PS1"

It's not perfect though:

但它并不完美:

  • If historydoesn't register the command (e.g. repeated or ignored commands), the start time will be wrong.
  • Multi-line commands don't get the date extracted properly from history.
  • 如果history不注册命令(例如重复或忽略命令),则开始时间将是错误的。
  • 多行命令无法从history.

回答by Ju Tutt

Here's my take on Thomas'

这是我对托马斯的看法

uses date +%s%3Nto get milliseconds as base unit, simplified following code (less zeros)

用于date +%s%3N以毫秒为基本单位,简化以下代码(更少的零)

function t_now {
    date +%s%3N
}

function t_start {
    t_start=${t_start:-$(t_now)}
}

function t_stop {
    local d_ms=$(($(t_now) - $t_start))
    local d_s=$((d_ms / 1000))
    local ms=$((d_ms % 1000))
    local s=$((d_s % 60))
    local m=$(((d_s / 60) % 60))
    local h=$((d_s / 3600))
    if ((h > 0)); then t_show=${h}h${m}m
    elif ((m > 0)); then t_show=${m}m${s}s
    elif ((s >= 10)); then t_show=${s}.$((ms / 100))s
    elif ((s > 0)); then t_show=${s}.$((ms / 10))s
    else t_show=${ms}ms
    fi
    unset t_start
}
set_prompt () {
t_stop
}

trap 't_start' DEBUG
PROMPT_COMMAND='set_prompt' 

Then add $t_showto your PS1

然后添加$t_show到您的 PS1

回答by Kamil Renczewski

Another approach for bash 4.x and above would be to use coprocwith PS0and PS1like below:

对于bash 4.x和上面的另一种方法是使用coprocPS0PS1象下面这样:

cmd_timer()
{
    echo $(( SECONDS - $(head -n1 <&"${CMD_TIMER[0]}") ))
}

coproc CMD_TIMER ( while read; do echo $SECONDS; done )
echo '' >&"${CMD_TIMER[1]}" # For value to be ready on first PS1 expansion
export PS0="$(echo '' >&${CMD_TIMER[1]})"
export PS1="[ $(cmd_timer) ] $"

This is a .bashrcready snippet. It is especially useful for everyone that uses undistract-mewhich overwrites trap DEBUGfor its own purposes.

这是一个.bashrc现成的片段。它对于使用undistract-me 的每个人都特别有用,它会trap DEBUG出于自己的目的进行覆盖。

回答by Mila Nautikus

this is my version

这是我的版本

  • use date to format time, only calc days
  • set terminal title
  • use \$ in PS1 for user $ + root #
  • show return code / exit code
  • use date -u to disable DST
  • use hidden names like _foo
  • 使用日期格式化时间,仅计算天数
  • 设置终端标题
  • 在 PS1 中为用户 $ + root 使用 \$ #
  • 显示返回码/退出码
  • 使用 date -u 禁用夏令时
  • 使用像 _foo 这样的隐藏名称
_x_dt_min=1 # minimum running time to show delta T
function _x_before {
    _x_t1=${_x_t1:-$(date -u '+%s.%N')} # float seconds
}
function _x_after {
    _x_rc=$? # return code
    _x_dt=$(echo $(date -u '+%s.%N') $_x_t1 | awk '{printf "%f",  - }')
    unset _x_t1
    #_x_dt=$(echo $_x_dt | awk '{printf "%f",  + 86400 * 1001}') # test
    # only show dT for long-running commands
    # ${f%.*} = int(floor(f))
    (( ${_x_dt%.*} >= $_x_dt_min )) && {
        _x_dt_d=$((${_x_dt%.*} / 86400))
        _x_dt_s='' # init delta T string
        (( $_x_dt_d > 0 )) && \
            _x_dt_s="${_x_dt_s}${_x_dt_d} days + "
        # format time
        # %4N = four digits of nsec
        _x_dt_s="${_x_dt_s}$(date -u -d0+${_x_dt}sec '+%T.%4N')"
        PS1='rc = ${_x_rc}\ndT = ${_x_dt_s}\n$ '
    } || {
        PS1='rc = ${_x_rc}\n$ '
    }
    # set terminal title to terminal number
    printf "3]0;%s
$ sleep 0.5
rc = 0
$ sleep 1
rc = 0
dT = 00:00:01.0040
$ sleep 1001d
rc = 0
dT = 1001 days + 00:00:00.0713
$ false
rc = 1
$ 
7" $(tty | sed 's|^/dev/\(pts/\)\?||') } trap '_x_before' DEBUG PROMPT_COMMAND='_x_after' PS1='$ '

sample output:

示例输出:

##代码##