代码之家  ›  专栏  ›  技术社区  ›  Mr Fooz

如何将最后一个命令的墙时间放入bash提示中?

  •  56
  • Mr Fooz  · 技术社区  · 15 年前

    是否有方法将上一个命令的已用墙时间嵌入到 Bash 提示?我希望有这样的东西:

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

    背景

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

    最好是自动的 时间 每一个交互命令和时间信息打印在几个字符,而不是3行。

    10 回复  |  直到 6 年前
        1
  •  10
  •   Hanxue    6 年前

    您可以使用这个zsh借用的钩子进行bash: http://www.twistedmatrix.com/users/glyph/preexec.bash.txt

    使用此挂钩完成计时(Mac OS X): Use Growl to monitor long-running shell commands

        2
  •  86
  •   Ville Laurikari    15 年前

    这是实现所需功能的最小独立代码:

    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]$ '
    
        3
  •  17
  •   Nicolas Thery    9 年前

    使用您的回复和其他一些线程,我编写了这个提示,希望与您共享。我在威奇拍了一张截图,你可以看到:

    • 白色:最后一个返回代码
    • 绿色和勾号表示成功(返回代码为0)
    • 红色和十字标记表示错误(返回代码为>0)
    • (绿色或红色):括号中的上次命令执行时间
    • (绿色或红色):当前日期时间(\t)
    • (如果不是根,则为绿色;如果是根,则为红色):记录的用户名
    • (绿色):服务器名称
    • (蓝色):pwd目录和常规目录$

    Custom prompt

    下面是要放入~/.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='\342\234\227'
        Checkmark='\342\234\223'
    
    
        # 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'
    
        4
  •  11
  •   willdye    15 年前

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

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

    这显示自上一个简单命令启动以来的秒数。如果您只是在不输入命令的情况下按Enter键,则计数器不会重置——当您只想查看自上次在其中执行任何操作以来终端已经运行了多长时间时,这一点非常方便。戴着红帽子和Ubuntu对我来说很好。在Cygwin下,它对我来说不起作用,但我不确定这是一个bug,还是仅仅是试图在Windows下运行bash的一个限制。

    这种方法的一个可能缺点是您一直在重置秒数,但是如果您确实需要保留秒数作为自初始shell调用以来的秒数,那么您可以为ps1计数器创建自己的变量,而不是直接使用秒数。另一个可能的缺点是,较大的秒值(如“999999”)可能更好地显示为days+hours+minutes+seconds,但很容易添加一个简单的筛选器,如:

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

    这将“999999”转换为“11天,13:46:39”。末尾的sed将“1days”更改为“1day”,并修剪空的前导值,如“0days,00:”。适应口味。

        5
  •  6
  •   philsnow    14 年前

    如果在你开始你的长期工作之前,你没有设置任何其他答案,你只想知道这项工作花了多长时间,你可以做简单的

    $ HISTTIMEFORMAT="%s " history 2
    

    它会用类似

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

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

        6
  •  4
  •   Thomas    9 年前

    我从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]$ '
    

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

        7
  •  3
  •   PJSCopeland    9 年前

    我发现 trap ... DEBUG 每次都在跑步 $PROMPT_COMMAND 被调用,重置计时器,因此始终返回0。

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

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

    但这并不完美:

    • 如果 历史 不注册命令(例如重复或忽略的命令),启动时间将出错。
    • 多行命令无法从中正确提取日期 历史 .
        8
  •  0
  •   Andrew Stein    15 年前

    PS1中的A \t对您有用吗?

    它没有给出经过的时间,但在必要时应该足够容易地减去时间。

    $ export PS1='[\t] [\w]\$ '
    [14:22:30] [/bin]$ sleep 10
    [14:22:42] [/bin]$
    

    根据操作人员的评论,他已经在使用\t。 如果可以使用tcsh而不是bash,则可以设置时间变量。

    /bin 1 > set time = 0
    /bin 2 > sleep 10
    0.015u 0.046s 0:10.09 0.4%      0+0k 0+0io 2570pf+0w
    /bin 3 >
    

    您可以将打印格式更改为不那么难看(参见tcsh手册页)。

    /bin 4 > set time = ( 0 "last: %E" )
    /bin 5 > sleep 10
    last: 0:10.09
    /bin 6 >
    

    我不知道巴什有类似的设施

        9
  •  0
  •   Zoe - Save the data dump 张群峰    6 年前

    这是我的版本

    • 使用日期格式化时间,仅计算天数
    • 设置终端标题
    • 在ps1中为用户$+root使用\$#
    • 显示返回代码/退出代码
    • 使用日期-u禁用DST
    • 使用隐藏的名称,如\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", $1 - $2}')
        unset _x_t1
        #_x_dt=$(echo $_x_dt | awk '{printf "%f", $1 + 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 "\033]0;%s\007" $(tty | sed 's|^/dev/\(pts/\)\?||')
    }
    trap '_x_before' DEBUG
    PROMPT_COMMAND='_x_after'
    PS1='\$ '
    

    样品输出:

    $ 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
    $ 
    
        10
  •  0
  •   Ju Tutt    6 年前

    这是我对托马斯的看法

    使用 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' 
    

    然后添加 $t_show 到你的PS1