Как непрерывно выводить историю команд с временными метками на терминал?

9

Я использую простой псевдоним, чтобы включить «отслеживание» команд в одном или нескольких терминальных окнах:

alias trackmi='export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"'

Затем я просто помещаюtail -f свой файл .bash_history в другой терминал рабочей области, чтобы получить немедленную обратную связь. Я только что включил неограниченную историю и обновил свой формат истории ( export HISTTIMEFORMAT="[%F %T] ") в .bashrc . Конечно, historyкоманда отображает метки времени. Но формат файла истории сам по себе:

#1401234303
alias
#1401234486
cat ../.bashrc 

Как мне преобразовать время Unix и вывести всю команду в одну строку, как с historyкомандой, включая нумерацию:

578  [2014-05-27 19:45:03] alias
579  [2014-05-27 19:48:06] cat ../.bashrc 

... и следуй за этим. Или найти способ постоянно выводить вывод historyкоманды на терминал?

Сообщество
источник

Ответы:

7

С GNU awk:

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'
Стефан Шазелас
источник
Прекрасно работает, сначала отображается быстрее, если я обновлю другое решение, fn+1чтобы сравнить! Спасибо!
5

Вот конечный продукт в действии на xterm с разделенным экраном, по умолчанию оболочка по умолчанию работает всего за пару команд:

введите описание изображения здесь

Более грубый способ сделать это, чем показано на скриншоте, может выглядеть так:

PS1='$( { date ; fc -l -0 ; } >${TGT_PTY} )'$PS1

Где ${TGT_PTY}бы вы ни находились в ttyкоманде, когда запускаете интерактивную оболочку на экране, где вы хотите получить вывод. Или, на самом деле, вы можете использовать любой доступный для записи файл, так как он по сути является целью перенаправления файлов.

Я использую синтаксис pty для псевдотерминала, потому что я предполагаю, что это какой-то xterm, но вы могли бы так же легко выделить vt - и ваша потоковая история всегда является лишь CTRL-ALT-Fnключевой комбинацией. Если бы это был я, я мог бы объединить два понятия и сделать это сеансом screenили tmuxсеансом на выделенном виртуальном пути ... Но я отвлекся.

На недавно загруженной машине меня приветствует типичная /bin/loginподсказка на типичной gettyконсоли Linux . Я нажимаю, CTRL-ALT-F2чтобы получить доступ к менее типичной kmsconконсоли, которая ведет себя намного больше, xtermчем a tty. Я ввожу команду ttyи получаю в ответ /dev/pts/0.

Как правило, xterms мультиплексирует одно оконечное устройство на несколько, используя псевдотерминалы - поэтому, если бы вы делали подобное в X11, переключаясь между вкладками клемм или окнами, вы, вероятно, также получили бы вывод /dev/pts/[0-9]*. Но виртуальные терминальные консоли, к которым CTRL-ALT-Fnполучают доступ с помощью комбинаций клавиш, являются истинными (эр) терминальными устройствами и поэтому получают свое собственное /dev/tty[0-9]*обозначение.

Вот почему после входа в консоль 2, когда я печатаю ttyв командной строке, ответ, /dev/pts/0но когда я делаю то же самое в консоли 1, вывод /dev/tty1. В любом случае обратно на консоль 2 я потом делаю:

bash
PS1='$( { date ; fc -l -0 ; } >/dev/tty1 )'$PS1

Там нет заметного эффекта. Я продолжаю набирать еще несколько команд, а затем переключаюсь на консоль 1, CTRL-ALT-F1снова нажимая . И там я нахожу повторяющиеся записи, которые похожи <date_time>\n<hist#>\t<hist_cmd_string>на каждую команду, которую я набрал на консоли 2.

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

TGT_PTY=
mkfifo ${TGT_PTY:=/tmp/shell.history.pipe}
{   echo 'OPENED ON:'
    date
} >${TGT_PTY}

И тогда, может быть ...

less +F ${TGT_PTY}

Команда грубого приглашения не соответствует вашим спецификациям - для нее нет ни строки dateформатирования, ни параметров форматирования fc- но ее механизм не требует много: каждый раз, когда ваш запрос отображает последнюю команду истории, а текущая дата и время записываются в ${TGT_PTY}файл , который вы укажете. Это так просто.

Просмотр и распечатка истории оболочки является fcосновной целью. Это встроенная оболочка, даже если dateнет. Он zsh fcможет предоставить все возможные варианты форматирования, некоторые из которых применяются к временным меткам. И, конечно же , как Вы отмечаете выше, bash«s historyможет сделать то же самое.

В целях более чистого вывода вы можете использовать метод, который я лучше объяснил здесь, чтобы установить постоянную переменную отслеживания в текущей оболочке, несмотря на необходимость отслеживать ее и обрабатывать ее в подоболочках в последовательности подсказок.

Вот портативное средство форматирования в соответствии с вашими требованиями:

_HIST() { [ -z ${_LH#$1} ] ||
    { date "+${1}%t[%F %T]"
      fc -nl -0 
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

: "${_LH=0}"
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

Я реализую счетчик last_history,$_LH который просто отслеживает последние обновления, чтобы вы не выписывали одну и ту же команду истории дважды - например, только для нажатия клавиши enter. Необходимо немного поспорить, чтобы увеличить переменную в текущей оболочке, чтобы она сохраняла свое значение, даже если функция вызывается в подоболочке - что, опять же, лучше объяснено в ссылке .

Его вывод выглядит как <hist#>\t[%F %T]\t<hist_cmd>\n

Но это только полностью портативная версия. С bashэтим можно справиться с меньшими затратами и реализовать только встроенные функции оболочки - что, вероятно, желательно, если учесть, что эта команда будет запускаться при каждом нажатии [ENTER]. Вот два способа:

_HIST() { [ -z ${_LH#$1} ] || {
        printf "${1}\t[%(%F %T)T]"
        fc -nl -0
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}
PROMPT_COMMAND=': ${_LH=0};'$PROMPT_COMMAND
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

Кроме того, используя команду bash's history, вы можете определить _HISTфункцию следующим образом:

_HIST() { [ -z ${_LH#$1} ] || 
        HISTTIMEFORMAT="[%F %T]<tab>" \
        history 1 >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

Вывод для любого метода также выглядит следующим образом: <hist#>\t[%F %T]\t<hist_cmd>\nхотя historyметод включает в себя несколько ведущих пробелов. Тем не менее, я полагаю, что historyвременные метки метода будут более точными, так как я не думаю, что им нужно будет ждать завершения указанной команды, прежде чем получить их штамп.

Вы можете избежать отслеживания любого состояния в обоих случаях, если только вы каким-то образом фильтруете поток uniq- как вы могли бы сделать, mkfifoкак я упоминал ранее.

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

Вы также можете сделать что-то похожее на то, что вы делаете, tailно скорее установить

HISTFILE=${TGT_PTY}
mikeserv
источник
Я на самом деле редактирую это в другой вкладке ... Больше времени, пожалуйста?
mikeserv
Ладно, сейчас я нажму "Сохранить", @lightÉ - но ты увидишь, где я остановился ...
mikeserv
@ подсветка - кстати - и я надеюсь, что я прав в этом - я предполагаю, что вы ввели команду, как написано, ${TGT_PTY}и все? Если это так, это объясняет «неоднозначное перенаправление», потому что это будет пустая переменная. Вам нужен файл. /dev/pts/[num]по всей вероятности , -
mikeserv
Это работает! Принтскрин помог! Я хотел бы, чтобы ваш первый и единственный блок кода был тем, что вы положили на экран печати, и четкой ссылкой на выбор того, на какие пункты вы хотите выводить данные, и ввода вкладки в функцию - больше ничего не требуется. Использование переменной для описания чего-то, что я должен был ввести вручную, чтобы попробовать это быстро, на мой взгляд, не очень хорошая практика, так как вам нужно копаться в тексте, чтобы понять все это, что затемняет решение. Также всего наилучшего вам и вашей семье.
@ LightingE - не важно, catя был параноиком. Работает нормально - даже через 12 часов.
mikeserv
4

Не стесняйтесь играть с форматированием, но это (я полагаю) делает то, что вы просите ... сохранить где-нибудь в вашем PATH, сделать исполняемым и наслаждаться:

#!/bin/bash
count=$(  echo "scale=0 ; $(cat ~/.bash_history | wc -l ) / 2" | bc -l )
tail -f ~/.bash_history | awk -v c=$count '{if($1 ~/^#/){gsub(/#/, "", $1);printf "%s\t", c; "date \"+%F %T\" --date @" $1 | getline stamp; printf "[%s]\t",stamp;c++}else{print $0}}'

Я уверен, что это можно оптимизировать, но вы поняли идею.

Краткое объяснение: поскольку ~ / .bash_history не отслеживает количество, мы сначала определяем количество записей. Затем немного волшебства awk, чтобы получить правильное форматирование и отслеживать количество записей.

Дзынь
источник
Это не работает, если есть многострочные записи. Также tail -fбудет читать 10 строк изначально, которые уже были включены в ваш count. Предполагается дата GNU в среде, отличной от POSIX (POSIXLY_CORRECT не установлена). Он запускает одну команду оболочки и одну команду даты на отметку времени.
Стефан Шазелас
@StephaneChazelas Для многострочных записей на моей установке они, кажется, регистрируются в обоих решениях. Может быть, что-то в моей настройке?
1
@lightÉ, tink countрассчитывает половину строки .bash_history, а затем увеличивается для каждой строки, не начинающейся с #, так что сообщаемый счетчик истории, вероятно, будет неправильным. Использование count=$(grep -c '^#' ...), вероятно, было бы лучше, но в любом случае эти номера истории, скорее всего, не синхронизируются, особенно если у вас одновременно запущено более двух bash.
Стефан Шазелас
@StephaneChazelas С уважением, поскольку я не могу полностью оценить ни одно из решений, я очень благодарен за объяснение! На самом деле количество отличается, поэтому здесь намного больше ... Я вижу, что вы создали собственное решение за время strftime, которое в основном используется historyкомандой.
1
Спасибо за обратную связь @StephaneChazelas, я посмотрю, смогу ли я обойти их :)
tink