Как можно указать время стены последней команды в приглашении Bash? - PullRequest
58 голосов
/ 07 декабря 2009

Есть ли способ встроить прошедшее время последней команды в приглашение Bash ? Я надеюсь на то, что выглядело бы так:

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

Фон

Я часто выполняю длинные задания обработки данных, и полезно знать, сколько времени они заняли, чтобы я мог оценить, сколько времени это займет для будущих заданий. Для очень регулярных задач я продолжаю записывать эту информацию строго, используя соответствующие методы регистрации. Для менее формальных задач я просто добавлю команду с time.

Было бы неплохо автоматически time каждой отдельной интерактивной команде и иметь информацию о времени, напечатанную в несколько символов, а не в 3 строки.

Ответы [ 10 ]

91 голосов
/ 07 декабря 2009

Это минимальный автономный код для достижения того, что вы хотите:

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]$ '
18 голосов
/ 15 января 2016

Используя ваши ответы и некоторые другие темы, я написал это приглашение, которым хочу поделиться с вами. Я сделал снимок экрана, на котором вы видите:

  • Белый: последний код возврата
  • Зеленый и галочка означают успех (код возврата был 0)
  • Красный и крестик означают ошибку (код возврата был> 0)
  • (зеленый или красный): время выполнения последней команды в скобках
  • (зеленый или красный): текущая дата и время (\ t)
  • (Зеленый, если не root, Красный, если root): зарегистрированное имя пользователя
  • (зеленый): имя сервера
  • (синий): каталог 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'
11 голосов
/ 28 апреля 2010

Еще один очень минимальный подход:

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

Показывает количество секунд с момента запуска последней простой команды. Счетчик не сбрасывается, если вы просто нажимаете Enter, не вводя команду - что может быть удобно, когда вы просто хотите увидеть, как долго работал терминал с тех пор, как вы в последний раз что-то в нем делали. Это прекрасно работает для меня в Red Hat и Ubuntu. Это не работает для меня под Cygwin, но я не уверен, является ли это ошибкой или просто ограничением попытки запуска Bash под Windows.

Одним из возможных недостатков этого подхода является то, что вы продолжаете сбрасывать SECONDS, но если вам действительно нужно сохранить SECONDS в качестве количества секунд с момента первоначального вызова оболочки, вы можете создать собственную переменную для счетчика 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:». Отрегулируйте по вкусу.

10 голосов
/ 07 декабря 2009

Вы можете использовать этот зацепленный zsh хук для bash: http://www.twistedmatrix.com/users/glyph/preexec.bash.txt

Время выполнения этой ловушки (Mac OS X): Использование Growl для отслеживания длительных команд оболочки

6 голосов
/ 08 июля 2010

Если вы не задали ни одного из других ответов до того, как приступили к своей продолжительной работе, и вы просто хотите узнать, сколько времени заняла работа, вы можете сделать простой

$ HISTTIMEFORMAT="%s " history 2

и он ответит чем-то вроде

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

и затем вы можете просто визуально вычесть две временные метки (кто-нибудь знает, как захватить вывод команды встроенной истории оболочки?)

4 голосов
/ 29 июля 2015

Я взял ответ от 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]$ '

Конечно, это требует запуска процесса, поэтому он менее эффективен, но все же достаточно быстр, чтобы вы не заметили.

3 голосов
/ 13 апреля 2016

Я обнаружил, что 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.
0 голосов
/ 21 декабря 2018

Вот мой взгляд на Томаса '

использует 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

.
0 голосов
/ 09 декабря 2018

это моя версия

  • использовать дату для форматирования времени, только расчетные дни
  • установить заголовок терминала
  • используйте \ $ в PS1 для пользователя $ + root #
  • показать код возврата / код выхода
  • используйте date -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
$ 
0 голосов
/ 07 декабря 2009

Поможет ли вам положить \ t в PS1?

Это не дает истекшего времени, но должно быть достаточно легко вычесть времена, когда это необходимо.

$ 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 >

Я не знаю подобного объекта в bash

...