Сохранение текущего каталога в историю Bash - PullRequest
58 голосов
/ 03 июня 2009

Я хотел бы сохранить текущий каталог, в котором каждая команда была введена вместе с командой в истории. Чтобы не все испортить, я думал о добавлении текущего каталога в качестве комментария в конце строки. Пример может помочь:

$ cd /usr/local/wherever
$ grep timmy accounts.txt

Я бы хотел, чтобы bash сохранил последнюю команду как:

grep timmy accounts.txt # /usr/local/wherever

Идея состоит в том, что таким образом я мог сразу увидеть, где я дал команду.

Ответы [ 7 ]

47 голосов
/ 04 июня 2009

Однострочная версия

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

Добавьте в файл ~/.bashrc следующее:

export PROMPT_COMMAND='hpwd=$(history 1); hpwd="${hpwd# *[0-9]*  }"; if [[ ${hpwd%% *} == "cd" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd="${hpwd% ### *} ### $cwd"; history -s "$hpwd"'

Это делает запись в истории, которая выглядит следующим образом:

rm subdir/file ### /some/dir

Я использую ### в качестве разделителя комментариев, чтобы отделить его от комментариев, которые может вводить пользователь, и уменьшить вероятность коллизий при удалении старых комментариев к путям, которые в противном случае накапливались бы при нажатии клавиши ввода в пустой командной строке. К сожалению, побочным эффектом является то, что команда типа echo " ### " искажается, хотя это должно быть довольно редко.

Некоторые люди сочтут тот факт, что я использую одно и то же имя переменной, неприятно. Обычно я бы не стал, но здесь я пытаюсь свести к минимуму след. Это легко изменить в любом случае.

Это слепо предполагает, что вы не используете HISTTIMEFORMAT или не изменяете историю каким-либо другим способом. Было бы легко добавить команду date в комментарий вместо функции HISTTIMEFORMAT. Однако, если вам нужно использовать его по какой-то причине, он все еще работает в подоболочке, так как он автоматически сбрасывается:

$ htf="%Y-%m-%d %R "    # save it for re-use
$ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25

Есть пара очень маленьких проблем с этим. Один из них, если вы используете команду history, например, такую:

$ history 3
echo "hello world" ### /home/dennis
ls -l /tmp/file ### /home/dennis
history 3

Результат не будет отображать комментарий к самой команде history, даже если вы увидите его, если нажмете стрелку вверх или введете другую команду history.

Другое - команды со встроенными символами новой строки оставляют некомментированную копию в истории в дополнение к закомментированной копии.

Могут появиться другие проблемы. Дайте мне знать, если найдете.

Как это работает

Bash выполняет команду, содержащуюся в переменной PROMPT_COMMAND, каждый раз, когда выдается первичное приглашение PS1. Этот маленький скрипт использует эту возможность, чтобы захватить последнюю команду в истории, добавить комментарий и сохранить ее обратно.

Здесь он разбит на части с комментариями:

hpwd=$(history 1)              # grab the most recent command
hpwd="${hpwd# *[0-9]*  }"      # strip off the history line number
if [[ ${hpwd%% *} == "cd" ]]   # if it's a cd command, we want the old directory
then                           #   so the comment matches other commands "where *were* you when this was done?"
    cwd=$OLDPWD
else
    cwd=$PWD
fi
hpwd="${hpwd% ### *} ### $cwd" # strip off the old ### comment if there was one so they 
                               #   don't accumulate, then build the comment
history -s "$hpwd"             # replace the most recent command with itself plus the comment
17 голосов
/ 07 июня 2009

hcmnt - длинная функциональная версия

Вот длинная версия в виде функции. Это монстр , но он добавляет несколько полезных функций. Я также разместил однострочник (оригинал) и более короткую функцию. Мне нравятся версии функций, потому что они не будут поглощать другие переменные в вашей среде и они намного более читабельны, чем однострочные. Прочитайте запись для однострочника и комментарии в функции ниже для получения дополнительной информации о том, как это работает и некоторые ограничения. Я разместил каждую версию в своем ответе, чтобы все было более организованно.

Чтобы использовать это, сохраните его в файле с именем hcmnt в таком месте, как /usr/local/bin (вы можете chmod +x, если хотите), а затем отправьте его в ~/.bashrc, например:

source /usr/local/bin/hcmnt
export hcmntextra='date "+%Y%m%d %R"'
export PROMPT_COMMAND='hcmnt'

Не редактируйте файл функции, в котором установлены PROMPT_COMMAND или hcmntextra. Оставьте их как есть, чтобы они остались по умолчанию. Включите их в .bashrc, как показано выше, и отредактируйте их там, чтобы установить параметры для hcmnt или изменить или сбросить hcmntextra. В отличие от короткой функции, для этой функции необходимо, чтобы обе переменные hcmntextra устанавливали , а использовали опцию -e, чтобы эта функция работала.

Вы можете добавить несколько параметров, которые задокументированы (с парой примеров) в комментариях в функции. Одна примечательная особенность заключается в том, чтобы запись истории с добавленным комментарием регистрировалась в файле и оставляла фактическую историю без изменений. Для того, чтобы использовать эту функцию, просто добавьте -l имя файла , например, так:

export PROMPT_COMMAND="hcmnt -l ~/histlog"

Вы можете использовать любую комбинацию опций, за исключением того, что -n и -t являются взаимоисключающими.

#!/bin/bash
hcmnt() {

# adds comments to bash history entries (or logs them)

# by Dennis Williamson - 2009-06-05 - updated 2009-06-19
# /704541/sohranenie-tekuschego-kataloga-v-istoriy-bash
# (thanks to Lajos Nagy for the idea)

# the comments can include the directory
# that was current when the command was issued
# plus optionally, the date or other information

# set the bash variable PROMPT_COMMAND to the name
# of this function and include these options:

    # -e - add the output of an extra command contained in the hcmntextra variable
    # -i - add ip address of terminal that you are logged in *from*
    #      if you're using screen, the screen number is shown
    #      if you're directly logged in, the tty number or X display number is shown
    # -l - log the entry rather than replacing it in the history
    # -n - don't add the directory
    # -t - add the from and to directories for cd commands
    # -y - add the terminal device (tty)
    # text or a variable

# Example result for PROMPT_COMMAND='hcmnt -et $LOGNAME'
#     when hcmntextra='date "+%Y%m%d %R"'
# cd /usr/bin ### mike 20090605 14:34 /home/mike -> /usr/bin

# Example for PROMPT_COMMAND='hcmnt'
# cd /usr/bin ### /home/mike

# Example for detailed logging:
#     when hcmntextra='date "+%Y%m%d %R"'
#     and PROMPT_COMMAND='hcmnt -eityl ~/.hcmnt.log $LOGNAME@$HOSTNAME'
#     $ tail -1 ~/.hcmnt.log
#     cd /var/log ### dave@hammerhead /dev/pts/3 192.168.1.1 20090617 16:12 /etc -> /var/log


# INSTALLATION: source this file in your .bashrc

    # will not work if HISTTIMEFORMAT is used - use hcmntextra instead
    export HISTTIMEFORMAT=

    # HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically:

    #   $ htf="%Y-%m-%d %R "    # save it for re-use
    #   $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25

    local script=$FUNCNAME

    local hcmnt=
    local cwd=
    local extra=
    local text=
    local logfile=

    local options=":eil:nty"
    local option=
    OPTIND=1
    local usage="Usage: $script [-e] [-i] [-l logfile] [-n|-t] [-y] [text]"

    local newline=$'\n' # used in workaround for bash history newline bug
    local histline=     # used in workaround for bash history newline bug

    local ExtraOpt=
    local LogOpt=
    local NoneOpt=
    local ToOpt=
    local tty=
    local ip=

    # *** process options to set flags ***

    while getopts $options option
    do
        case $option in
            e ) ExtraOpt=1;;        # include hcmntextra
            i ) ip="$(who --ips -m)" # include the terminal's ip address
                ip=($ip)
                ip="${ip[4]}"
                if [[ -z $ip ]]
                then
                    ip=$(tty)
                fi;;
            l ) LogOpt=1            # log the entry
                logfile=$OPTARG;;
            n ) if [[ $ToOpt ]]
                then
                    echo "$script: can't include both -n and -t."
                    echo $usage
                    return 1
                else
                    NoneOpt=1       # don't include path
                fi;;
            t ) if [[ $NoneOpt ]]
                then
                    echo "$script: can't include both -n and -t."
                    echo $usage
                    return 1
                else
                    ToOpt=1         # cd shows "from -> to"
                fi;;
            y ) tty=$(tty);;
            : ) echo "$script: missing filename: -$OPTARG."
                echo $usage
                return 1;;
            * ) echo "$script: invalid option: -$OPTARG."
                echo $usage
                return 1;;
        esac
    done

    text=($@)                       # arguments after the options are saved to add to the comment
    text="${text[*]:$OPTIND - 1:${#text[*]}}"

    # *** process the history entry ***

    hcmnt=$(history 1)              # grab the most recent command

    # save history line number for workaround for bash history newline bug
    histline="${hcmnt%  *}"

    hcmnt="${hcmnt# *[0-9]*  }"     # strip off the history line number

    if [[ -z $NoneOpt ]]            # are we adding the directory?
    then
        if [[ ${hcmnt%% *} == "cd" ]]    # if it's a cd command, we want the old directory
        then                             #   so the comment matches other commands "where *were* you when this was done?"
            if [[ $ToOpt ]]
            then
                cwd="$OLDPWD -> $PWD"    # show "from -> to" for cd
            else
                cwd=$OLDPWD              # just show "from"
            fi
        else
            cwd=$PWD                     # it's not a cd, so just show where we are
        fi
    fi

    if [[ $ExtraOpt && $hcmntextra ]]    # do we want a little something extra?
    then
        extra=$(eval "$hcmntextra")
    fi

    # strip off the old ### comment if there was one so they don't accumulate
    # then build the string (if text or extra aren't empty, add them plus a space)
    hcmnt="${hcmnt% ### *} ### ${text:+$text }${tty:+$tty }${ip:+$ip }${extra:+$extra }$cwd"

    if [[ $LogOpt ]]
    then
        # save the entry in a logfile
        echo "$hcmnt" >> $logfile || echo "$script: file error." ; return 1
    else

        # workaround for bash history newline bug
        if [[ $hcmnt != ${hcmnt/$newline/} ]] # if there a newline in the command
        then
            history -d $histline # then delete the current command so it's not duplicated
        fi

        # replace the history entry
        history -s "$hcmnt"
    fi

} # END FUNCTION hcmnt

# set a default (must use -e option to include it)
export hcmntextra='date "+%Y%m%d %R"'      # you must be really careful to get the quoting right

# start using it
export PROMPT_COMMAND='hcmnt'

обновление 2009-06-19 : добавлены опции, полезные для ведения журнала (ip и tty), обходной путь для проблемы с повторяющимся вводом, удалены посторонние нулевые назначения

13 голосов
/ 31 августа 2014

Вы можете установить Advanced Shell History , инструмент с открытым исходным кодом, который записывает вашу историю bash или zsh в базу данных sqlite. Он записывает такие вещи, как текущий рабочий каталог, код завершения команды, время запуска и остановки команды, время начала и окончания сеанса, tty и т. Д.

Если вы хотите запросить базу данных истории, вы можете написать свои собственные запросы SQL, сохранить их и сделать их доступными в комплекте инструмента ash_query. Есть несколько полезных предварительно упакованных запросов, но, поскольку я достаточно хорошо знаю SQL, я обычно просто открываю базу данных и делаю запросы в интерактивном режиме, когда мне нужно что-то искать.

Один запрос, который я нахожу очень полезным, - это просмотр истории текущего рабочего каталога. Это помогает мне вспомнить, где я остановился, когда работал над чем-то.

vagrant@precise32:~$ ash_query -q CWD
session
    when                   what
1
    2014-08-27 17:13:07    ls -la
    2014-08-27 17:13:09    cd .ash
    2014-08-27 17:16:27    ls
    2014-08-27 17:16:33    rm -rf advanced-shell-history/
    2014-08-27 17:16:35    ls
    2014-08-27 17:16:37    less postinstall.sh
    2014-08-27 17:16:57    sudo reboot -n

И та же история с использованием текущего рабочего каталога (и всего, что под ним):

vagrant@precise32:~$ ash_query -q RCWD
session
    where
        when                   what
1
    /home/vagrant/advanced-shell-history
        2014-08-27 17:11:34    nano ~/.bashrc
        2014-08-27 17:12:54    source /usr/lib/advanced_shell_history/bash
        2014-08-27 17:12:57    source /usr/lib/advanced_shell_history/bash
        2014-08-27 17:13:05    cd
    /home/vagrant
        2014-08-27 17:13:07    ls -la
        2014-08-27 17:13:09    cd .ash
    /home/vagrant/.ash
        2014-08-27 17:13:10    ls
        2014-08-27 17:13:11    ls -l
        2014-08-27 17:13:16    sqlite3 history.db
        2014-08-27 17:13:43    ash_query
        2014-08-27 17:13:50    ash_query -Q
        2014-08-27 17:13:56    ash_query -q DEMO
        2014-08-27 17:14:39    ash_query -q ME
        2014-08-27 17:16:26    cd
    /home/vagrant
        2014-08-27 17:16:27    ls
        2014-08-27 17:16:33    rm -rf advanced-shell-history/
        2014-08-27 17:16:35    ls
        2014-08-27 17:16:37    less postinstall.sh
        2014-08-27 17:16:57    sudo reboot -n

FWIW - я автор и сопровождающий проекта.

6 голосов
/ 07 июня 2009

hcmnts - короткая версия функции

Вот краткая версия в виде функции. Я также разместил однострочник (оригинал) и более длинную функцию с несколькими добавленными функциями. Мне нравятся версии функций, потому что они не будут поглощать другие переменные в вашей среде, и они намного более читабельны, чем однострочные. Прочитайте запись для однострочника для получения дополнительной информации о том, как это работает и некоторые ограничения. Я разместил каждую версию в своем ответе, чтобы все было более организованно.

Чтобы использовать это, сохраните его в файле с именем hcmnts в таком месте, как /usr/local/bin (вы можете chmod +x, если хотите), затем отправьте его в ~/.bashrc, например:

source /usr/local/bin/hcmnts

Закомментируйте строку, которая устанавливает hcmntextra, если вам не нужны дата и время (или вы можете изменить ее формат или использовать какую-либо другую команду, кроме date).

Это все, что нужно.

#!/bin/bash
hcmnts() {
    # adds comments to bash history entries

    # the *S*hort version of hcmnt (which has many more features)

    # by Dennis Williamson
    # /704541/sohranenie-tekuschego-kataloga-v-istoriy-bash
    # (thanks to Lajos Nagy for the idea)

    # INSTALLATION: source this file in your .bashrc

    # will not work if HISTTIMEFORMAT is used - use hcmntextra instead
    export HISTTIMEFORMAT=

    # HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically:

    #   $ htf="%Y-%m-%d %R "    # save it for re-use
    #   $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25

    local hcmnt
    local cwd
    local extra

    hcmnt=$(history 1)
    hcmnt="${hcmnt# *[0-9]*  }"

    if [[ ${hcmnt%% *} == "cd" ]]
    then
        cwd=$OLDPWD
    else
        cwd=$PWD
    fi

    extra=$(eval "$hcmntextra")

    hcmnt="${hcmnt% ### *}"
    hcmnt="$hcmnt ### ${extra:+$extra }$cwd"

    history -s "$hcmnt"
}
export hcmntextra='date +"%Y%m%d %R"'
export PROMPT_COMMAND='hcmnts'
2 голосов
/ 03 октября 2014

Для тех, кто хочет это в zsh, я изменил Реализацию Jeet Sukumaran и percol, чтобы позволить интерактивный поиск по ключевым словам и извлечение либо команды, либо пути, в котором она была выполнена. Также возможно отфильтровать дубликат команды и скрыть поля (дата, команда, путь)

1 голос
/ 28 июня 2011

Джентльмен, это работает лучше. Единственное, что я не могу понять, это как заставить скрипт НЕ регистрироваться в syslog при входе в систему и регистрировать последнюю команду в истории. Но пока работает как шарм.

#!/bin/bash

trackerbash() {
    # adds comments to bash history entries

    # by Dennis Williamson
    # /704541/sohranenie-tekuschego-kataloga-v-istoriy-bash
    # (thanks to Lajos Nagy for the idea)

    #Supper Enhanced by QXT


    # INSTALLATION: source this file in your .bashrc

    export HISTTIMEFORMAT=
#    export HISTTIMEFORMAT='%F   %T    '

    local hcmnt
    local cwd
    local extra
    local thistty
    local whoiam
    local sudouser
    local shelldate
    local TRACKIP
    local TRACKHOST


            thistty=`/usr/bin/tty|/bin/cut -f3-4 -d/`
            whoiam=`/usr/bin/whoami`
            sudouser=`last |grep $thistty |head -1 | awk '{ print $1 }' |cut -c 1-10`
            hcmnt=$(history 1)
            hcmnt="${hcmnt# *[0-9]*  }"
            cwd=`pwd`



            hcmnt="${hcmnt% ### *}"
            hcmnt=" $hcmnt ${extra:+$extra }"

            shelldate=`date +"%Y %b %d %R:%S"`
            TRACKHOST=`whoami | sed -r "s/.*\((.*)\).*/\\1/"`
            TRACKIP=`last |grep $thistty |head -1 | awk '{ print $3 }'`


            logger -p local1.notice -t bashtracker -i -- "$sudouser ${USER}: $thistty: $TRACKIP: $shelldate: $cwd : $hcmnt"
            history -w 

}
export PROMPT_COMMAND='trackerbash'

0 голосов
/ 18 октября 2017

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

Кроме того, одна строка выше слишком сильно мешает вашему пользовательскому интерфейсу.

export PROMPT_COMMAND='if [ "$(id -u)" -ne 0 ]; then echo "$(date "+%Y-%m-%d.%H:%M:%S") $(pwd) $(history 1)" >> ~/.bash.log; fi'

Поскольку мой домашний каталог, как правило, представляет собой перекрестную монтируемую штуковину, это побочный эффект - история всего, что я когда-либо делал. При желании добавьте $(hostname) к команде echo выше ... в зависимости от вашей рабочей среды.

Даже при 100 тыс. Записей grep более чем достаточно. Нет необходимости sqlite войти. Просто не вводите пароли в командной строке, и все хорошо. В любом случае пароли - это технология 90-х годов!

Также для поиска я склонен делать это:

function hh() {
    grep "$1" ~/.bash.log
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...