将当前目录保存为bash历史记录

Laj*_*agy 60 bash

我想保存发布每个命令的当前目录以及历史记录中的命令.为了不搞乱,我考虑在行尾添加当前目录作为注释.一个例子可能有帮助:

$ cd /usr/local/wherever
$ grep timmy accounts.txt
Run Code Online (Sandbox Code Playgroud)

我希望bash将最后一个命令保存为:

grep timmy accounts.txt # /usr/local/wherever
Run Code Online (Sandbox Code Playgroud)

这个想法是这样我可以立即看到我发出命令的位置.

Pau*_*ce. 47

单线版

这是一个单行版本.这是原作.我还发布了一个简短的功能版本和一个带有几个附加功能的长功能版本.我喜欢这些函数版本,因为它们不会破坏你环境中的其他变量,并且它们比单行程更易读.这篇文章提供了一些关于它们如何工作的信息,这些信息在其他文章中可能不会重复.

将以下内容添加到您的~/.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"'
Run Code Online (Sandbox Code Playgroud)

这使得历史记录条目看起来像:

rm subdir/file ### /some/dir
Run Code Online (Sandbox Code Playgroud)

我使用###注释分隔符将其与用户可能键入的注释区分开来,并在剥离旧路径注释时减少冲突的可能性,否则如果在空白命令行上按Enter键则会累积.不幸的是,副作用是一个命令echo " ### "被破坏,虽然这应该是相当罕见的.

有些人会发现我重用相同的变量名称是令人不愉快的.通常我不会,但在这里我试图尽量减少足迹.在任何情况下都很容易改变.

它盲目地假设您没有以HISTTIMEFORMAT其他方式使用或修改历史记录.将date命令添加到注释以代替该HISTTIMEFORMAT功能将很容易.但是,如果由于某种原因需要使用它,它仍然可以在子shell中工作,因为它会自动取消设置:

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

它有几个非常小的问题.一种是如果你使用这样的history命令,例如:

$ history 3
echo "hello world" ### /home/dennis
ls -l /tmp/file ### /home/dennis
history 3
Run Code Online (Sandbox Code Playgroud)

结果不会显示history命令本身的注释,即使您按下向上箭头或发出另一个history命令也会看到它.

另一个是,除了注释副本之外,具有嵌入换行符的命令在历史记录中留下未注释的副本.

可能还有其他问题出现.如果你发现任何问题,请告诉我.

这个怎么运作

PROMPT_COMMAND每次PS1发出主要提示时,Bash都会执行变量中包含的命令.这个小脚本利用它来获取历史记录中的最后一个命令,为其添加注释并将其保存回来.

在这里,它与评论分开:

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
Run Code Online (Sandbox Code Playgroud)


Pau*_*ce. 18

hcmnt - 长函数版本

这是一个函数形式的长版本.这是一个怪物,但它增加了一些有用的功能.我还发布了一个单行(原始)和一个较短的功能.我喜欢这些函数版本,因为它们不会破坏你环境中的其他变量,并且它们比单行程更易读.阅读下面函数中单行和通信的条目,以获取有关其工作原理和一些限制的其他信息.我已将每个版本发布在自己的答案中,以使事情更有条理.

要使用此文件,请将其保存hcmnt在一个位置调用的文件中/usr/local/bin(chmod +x如果需要,可以将其保存),然后将其源于~/.bashrc以下内容:

source /usr/local/bin/hcmnt
export hcmntextra='date "+%Y%m%d %R"'
export PROMPT_COMMAND='hcmnt'
Run Code Online (Sandbox Code Playgroud)

不要编辑功能文件的位置PROMPT_COMMANDhcmntextra设置.保持原样,使它们保持默认状态..bashrc如上所示将它们包括在您的内容中并在那里编辑它们以设置hcmnt或更改或取消设置的选项hcmntextra.与短函数不同,使用此函数您必须都hcmntextra设置变量使用该-e选项使该功能起作用.

您可以在函数的注释中添加几个已记录的选项(带有几个示例).一个值得注意的功能是将带有附加注释的历史记录条目记录到文件中,并保持实际历史记录不变.要使用此功能,只需添加-l 文件名选项,如下所示:

export PROMPT_COMMAND="hcmnt -l ~/histlog"
Run Code Online (Sandbox Code Playgroud)

您可以使用任何选项组合,但这些选项除外-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 $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'
Run Code Online (Sandbox Code Playgroud)

更新2009-06-19:添加了对日志记录(ip和tty)有用的选项,重复输入问题的解决方法,删除了无关的空分配


car*_*son 13

您可以安装Advanced Shell History,这是一个将您bashzsh历史记录写入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
Run Code Online (Sandbox Code Playgroud)

使用当前工作目录(及其下面的任何内容)的相同历史记录:

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
Run Code Online (Sandbox Code Playgroud)

FWIW - 我是该项目的作者和维护者.


Pau*_*ce. 6

hcmnts - 短功能版

这是一个函数形式的简短版本.我还发布了一个单行(原始)和一个更长的功能,增加了几个功能.我喜欢这些函数版本,因为它们不会破坏你环境中的其他变量,并且它们比单行程更易读.阅读单行的条目,了解有关其工作原理和一些限制的其他信息.我已将每个版本发布在自己的答案中,以使事情更有条理.

要使用此文件,请将其保存hcmnts在一个位置调用的文件中/usr/local/bin(chmod +x如果需要,可以将其保存),然后将其源于~/.bashrc以下内容:

source /usr/local/bin/hcmnts
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)