Подтвердить что ты не робот

Сохранение текущего каталога в bash истории

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

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

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

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

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

4b9b3361

Ответ 1

Однолинейная версия

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

Добавьте следующее в ваш файл ~/.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 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

Ответ 2

hcmnt - версия с длинными функциями

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

Чтобы использовать этот, сохраните его в файле с именем 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 filename следующим образом:

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
# http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
# (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 [email protected]$HOSTNAME'
#     $ tail -1 ~/.hcmnt.log
#     cd /var/log ### [email protected] /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 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=([email protected])                       # 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 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 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 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'

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

Ответ 3

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

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

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

[email protected]:~$ 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

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

[email protected]:~$ 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 - Я являюсь автором и сторонником проекта.

Ответ 4

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

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

Чтобы использовать этот, сохраните его в файле с именем 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
    # http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
    # (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'

Ответ 5

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

Ответ 6

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

#!/bin/bash

trackerbash() {
    # adds comments to bash history entries

    # by Dennis Williamson
    # http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
    # (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'

Ответ 7

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

Также однострочник выше слишком сильно портит ваш пользовательский интерфейс.

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, приведенной выше... в зависимости от вашей рабочей среды.

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

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

function hh() {
    grep "$1" ~/.bash.log
}

Ответ 8

Полное раскрытие: я автор FOSS-инструмента
shournal - журнал (file-) для вашей оболочки:
Используя интеграцию bash, рабочий каталог команды также хранится в shournal sqlite-database и может быть получен через

shournal --query -cmdcwd "$PWD"

Запросы для рабочих подкаталогов можно выполнить с помощью

shournal --query -cmdcwd -like "$PWD/%"