有没有办法在Bash提示符中嵌入最后一个命令的已用时间?我希望看到这样的东西:
[last: 0s][/my/dir]$ sleep 10
[last: 10s][/my/dir]$
背景
我经常进行长时间的数据处理工作,知道他们花了多长时间是有用的,所以我可以估计未来的工作需要多长时间。对于非常常规的任务,我会继续使用适当的日志记录技术严格记录这些信息。对于不太正式的任务,我只是在time
之前添加命令。
自动time
每个交互式命令并将时序信息打印成几个字符而不是三行,这将是很好的。
答案 0 :(得分:89)
这是实现您想要的最小的独立代码:
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]$ '
答案 1 :(得分:18)
使用您的回复和其他一些帖子,我写了这个提示,我想与您分享。我看了一下截图:
以下是放在〜/ .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'
答案 2 :(得分:11)
另一个非常小的方法是:
trap 'SECONDS=0' DEBUG
export PS1='your_normal_prompt_here ($SECONDS) # '
显示自上次简单命令启动以来的秒数。如果您只是在不输入命令的情况下按Enter键,则不会重置计数器 - 当您只想查看终端自上次执行任何操作以来已经运行了多长时间时,这可能非常方便。它适用于Red Hat和Ubuntu。它在Cygwin下对我不起作用,但我不确定这是一个bug还是仅仅是试图在Windows下运行Bash的限制。
这种方法的一个可能的缺点是你不断重置SECONDS,但是如果你真的需要将SECONDS保留为自初始shell调用以来的秒数,你可以为PS1计数器创建自己的变量而不是直接使用SECONDS。另一个可能的缺点是,诸如“999999”之类的大秒值可能会更好地显示为天+小时+分钟+秒,但是很容易添加一个简单的过滤器,例如:
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”翻译成“11days,13:46:39”。最后的sed将“1days”更改为“1day”,并修剪空的前导值,例如“0days,00:”。调整味道。
答案 3 :(得分:10)
您可以将这个zsh-borrowed钩子用于bash:http://www.twistedmatrix.com/users/glyph/preexec.bash.txt
使用此挂钩完成计时(Mac OS X):Use Growl to monitor long-running shell commands
答案 4 :(得分:6)
如果你在开始长期工作之前没有设置任何其他答案,而你只想知道这份工作需要多长时间,那么你就可以做到这一点
$ HISTTIMEFORMAT="%s " history 2
它将回复类似
的内容 654 1278611022 gvn up
655 1278611714 HISTTIMEFORMAT="%s " history 2
然后你可以直观地减去两个时间戳(任何人都知道如何捕获shell内置历史命令的输出?)
答案 5 :(得分:4)
我从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]$ '
当然这需要启动一个过程,因此效率较低,但仍然足够快,你不会注意到。
答案 6 :(得分:3)
我发现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"
虽然不完美:
history
没有注册命令(例如重复或忽略的命令),则开始时间将是错误的。history
正确提取日期。答案 7 :(得分:1)
bash 4.x及更高版本的另一种方法是将coproc
与PS0
和PS1
一起使用,如下所示:
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) ] \$"
这是一个.bashrc
准备好的代码段。
对于每个使用undistract-me出于自己的目的覆盖trap DEBUG
的人来说,它特别有用。
答案 8 :(得分:1)
zsh
的翻译版本。
附加到您的~/.zshrc
文件
function preexec() {
timer=$(date +%s%3N)
}
function precmd() {
if [ $timer ]; then
local now=$(date +%s%3N)
local d_ms=$(($now-$timer))
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 elapsed=${h}h${m}m
elif ((m > 0)); then elapsed=${m}m${s}s
elif ((s >= 10)); then elapsed=${s}.$((ms / 100))s
elif ((s > 0)); then elapsed=${s}.$((ms / 10))s
else elapsed=${ms}ms
fi
export RPROMPT="%F{cyan}${elapsed} %{$reset_color%}"
unset timer
fi
}
答案 9 :(得分:0)
将PS1中的\ t用于你?
它没有给出经过的时间,但在必要时应该很容易减去时间。
$ export PS1='[\t] [\w]\$ '
[14:22:30] [/bin]$ sleep 10
[14:22:42] [/bin]$
根据OP的评论,他已经在使用\ 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 >
我不知道bash中有类似的设施
答案 10 :(得分:0)
这是我的版本
_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
$
答案 11 :(得分:0)
这是我对托马斯的看法
使用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
答案 12 :(得分:0)
如果某人只想查看执行时间, 将此行添加到bash_profile
trap 'printf "t=%s\n" $(date +%T.%3N)' DEBUG
答案 13 :(得分:0)
受zsh spaceship prompt和Ville's answer启发,由this time conversion function启发的带有小时,分钟和秒的版本。
我还添加了阈值变量,以便计时器仅显示长时间运行的命令。
time_threshold=5;
function convert_secs {
((h=${1}/3600))
((m=(${1}%3600)/60))
((s=${1}%60))
if [ $h -gt 0 ]; then printf "${h}h "; fi
if [ $h -gt 0 ] || [ $m -gt 0 ]; then printf "${m}m "; fi
if [ $s -gt 0 ]; then printf "${s}s "; fi
}
function timer_start {
timer=${timer:-$SECONDS}
}
function timer_stop {
timer_time=$(($SECONDS - $timer))
if [ ! -z $timer_time ] && [ $timer_time -ge ${time_threshold} ]; then
timer_show="took $(convert_secs $timer_time)"
else
timer_show=""
fi
unset timer
}
trap 'timer_start' DEBUG
PROMPT_COMMAND=timer_stop
PS1='\n\w ${timer_show}\n\\$ '
对于我的屏幕截图中的彩色输出:
bold=$(tput bold)
reset=$(tput sgr0)
yellow=$(tput setaf 3)
cyan=$(tput setaf 6)
PS1='\n${bold}${cyan}\w ${yellow}${timer_show}${reset}\n\\$ '