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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/945288/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 20:55:57  来源:igfitidea点击:

Saving current directory to bash history

bash

提问by Lajos Nagy

I'd like to save the current directory where the each command was issued alongside the command in the history. In order not to mess things up, I was thinking about adding the current directory as a comment at the end of the line. An example might help:

我想保存在历史命令旁边发出每个命令的当前目录。为了不把事情搞砸,我正在考虑在行尾添加当前目录作为注释。一个例子可能有帮助:

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

I'd like bash to save the last command as:

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

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

The idea is that this way I could immediately see where I issued the command.

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

采纳答案by Paused until further notice.

One-liner version

单线版

Here is a one-liner version. It's the original. I've also posted a short function versionand a long function versionwith several added features. I like the function versions because they won't clobber other variables in your environment and they're much more readable than the one-liner. This post has some information on how they all work which may not be duplicated in the others.

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

Add the following to your ~/.bashrcfile:

将以下内容添加到您的~/.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"'

This makes a history entry that looks like:

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

rm subdir/file ### /some/dir

I use ###as a comment delimiter to set it apart from comments that the user might type and to reduce the chance of collisions when stripping old path comments that would otherwise accumulate if you press enter on a blank command line. Unfortunately, the side affect is that a command like echo " ### "gets mangled, although that should be fairly rare.

我将其###用作注释分隔符,以将其与用户可能键入的注释区分开来,并在剥离旧路径注释时减少冲突的可能性,否则在空白命令行上按 Enter 键时这些注释会累积。不幸的是,副作用是像这样的命令echo " ### "被破坏了,尽管这种情况应该很少见。

Some people will find the fact that I reuse the same variable name to be unpleasant. Ordinarily I wouldn't, but here I'm trying to minimize the footprint. It's easily changed in any case.

有些人会发现我重复使用相同的变量名是不愉快的。通常我不会,但在这里我试图尽量减少足迹。在任何情况下都可以轻松更改。

It blindly assumes that you aren't using HISTTIMEFORMATor modifying the history in some other way. It would be easy to add a datecommand to the comment in lieu of the HISTTIMEFORMATfeature. However, if you need to use it for some reason, it still works in a subshell since it gets unset automatically:

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

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

There are a couple of very small problems with it. One is if you use the historycommand like this, for example:

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

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

The result will not show the comment on the historycommand itself, even though you'll see it if you press up-arrow or issue another historycommand.

结果不会显示对history命令本身的评论,即使您在按向上箭头或发出另一个history命令时会看到它。

The other is that commands with embedded newlines leave an uncommented copy in the history in addition to the commented copy.

另一个是带有嵌入换行符的命令除了已注释的副本外,还会在历史记录中留下未注释的副本。

There may be other problems that show up. Let me know if you find any.

可能还会出现其他问题。如果你找到了,请告诉我。

How it works

这个怎么运作

Bash executes a command contained in the PROMPT_COMMANDvariable each time the PS1primary prompt is issued. This little script takes advantage of that to grab the last command in the history, add a comment to it and save it back.

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

Here it is split apart with comments:

这里用评论分开:

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

回答by Paused until further notice.

hcmnt - long function version

hcmnt - 长函数版本

Here is a long version in the form of a function. It's a monster, but it adds several useful features. I've also posted a one-liner (the original) and a shorter function. I like the function versions because they won't clobber other variables in your environment and they're much more readable than the one-liner. Read the entry for the one-liner and the commments in the function below for additional information on how it works and some limitations. I've posted each version in its own answer in order to keep things more organized.

这是一个函数形式的长版本。这是一个怪物,但它增加了几个有用的功能。我还发布了一个单行(原始)和一个较短的函数。我喜欢函数版本,因为它们不会破坏环境中的其他变量,而且它们比单行代码更具可读性。阅读 one-liner 的条目和下面函数中的注释,以获取有关其工作原理和一些限制的更多信息。为了让事情更有条理,我已经在自己的答案中发布了每个版本。

To use this one, save it in a file called hcmntin a location like /usr/local/bin(you can chmod +xit if you want) then source it in your ~/.bashrclike this:

要使用这个,请将其保存在一个名为的文件hcmnt/usr/local/binchmod +x如果需要,您可以使用它),然后~/.bashrc像这样将其来源:

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

Don't edit the function's file where PROMPT_COMMANDor hcmntextraare set. Leave them as is so they remain as defaults. Include them in your .bashrcas shown above and edit them there to set options for hcmntor to change or unset hcmntextra. Unlike the short function, with this one you must both have the hcmntextravariable set anduse the -eoption to make that feature work.

不要在设置PROMPT_COMMAND或的地方编辑函数的文件hcmntextra。将它们保留原样,以便它们保留为默认值。.bashrc如上所示将它们包含在您的文件中,并在那里编辑它们以设置hcmnt或 更改或取消设置的选项hcmntextra。与 short 函数不同,使用这个函数,您必须同时hcmntextra设置变量使用-e选项使该功能起作用。

You can add several options which are documented (with a couple of examples) in the comments in the function. One notable feature is to have the history entry with appended comment logged to a file and leave the actual history untouched. In order to use this function, just add the -lfilenameoption like so:

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

export PROMPT_COMMAND="hcmnt -l ~/histlog"

You can use any combination of options, except that -nand -tare mutually exclusive.

您可以使用任何选项组合,除了-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'

update 2009-06-19: Added options useful for logging (ip and tty), a workaround for the duplicate entry problem, removed extraneous null assignments

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

回答by carl.anderson

You could install Advanced Shell History, an open source tool that writes your bashor zshhistory to a sqlite database. This records things like the current working directory, the command exit code, command start and stop times, session start and stop times, tty, etc.

您可以安装Advanced Shell History,这是一种开源工具,可将您的bashzsh历史记录写入sqlite 数据库。这会记录诸如当前工作目录、命令退出代码、命令开始和停止时间、会话开始和停止时间、tty 等内容。

If you want to query the history database, you can write your own SQL queries, save them and make them available within the bundled ash_querytool. There are a few useful prepackaged queries, but since I know SQL pretty well, I usually just open the database and query interactively when I need to look for something.

如果您想查询历史数据库,您可以编写自己的 SQL 查询,保存它们并在捆绑ash_query工具中提供它们。有一些有用的预先打包的查询,但由于我对 SQL 非常了解,所以当我需要查找某些内容时,我通常只打开数据库并交互查询。

One query I find very useful, though, is looking at the history of the current working directory. It helps me remember where I left off when I was working on something.

不过,我发现一个非常有用的查询是查看当前工作目录的历史记录。它帮助我记住我在做某事时离开的地方。

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

And the same history using the current working directory (and anything below it):

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

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 - I'm the author and maintainer of the project.

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

回答by Paused until further notice.

hcmnts - short function version

hcmnts - 短功能版本

Here is a short version in the form of a function. I've also posted a one-liner (the original) and a longer function with several added features. I like the function versions because they won't clobber other variables in your environment and they're much more readable than the one-liner. Read the entry for the one-liner for additional information on how this works and some limitations. I've posted each version in its own answer in order to keep things more organized.

这是一个函数形式的简短版本。我还发布了一个单行(原始)和一个带有几个附加功能的更长的功能。我喜欢函数版本,因为它们不会破坏环境中的其他变量,而且它们比单行代码更具可读性。阅读 one-liner 的条目,了解有关其工作原理和一些限制的更多信息。为了让事情更有条理,我已经在自己的答案中发布了每个版本。

To use this one, save it in a file called hcmntsin a location like /usr/local/bin(you can chmod +xit if you want) then source it in your ~/.bashrclike this:

要使用这个,请将其保存在一个名为的文件hcmnts/usr/local/binchmod +x如果需要,您可以使用它),然后~/.bashrc像这样将其来源:

source /usr/local/bin/hcmnts

Comment out the line that sets hcmntextraif you don't want the date and time (or you can change its format or use some other command besides date).

hcmntextra如果您不需要日期和时间,请注释掉设置的行(或者您可以更改其格式或使用除 之外的其他命令date)。

That's all there is to it.

这里的所有都是它的。

#!/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'

回答by Gordon Wells

For those who want this in zsh I've modifiedJeet Sukumaran's implementation and percol to allow interactive keyword searching and extraction of either the command or path it was executed in. It's also possible to filter out duplicate commands and hide fields (date, command, path)

对于那些想要在 zsh 中使用它的人,我修改了Jeet Sukumaran 的实现和 percol,以允许交互式关键字搜索和提取执行它的命令或路径。还可以过滤掉重复的命令并隐藏字段(日期、命令、小路)

回答by Erik Aronesty

Here's a one liner of what I use. Sticking it here because it's vastly simpler, and I have no problem with per-session history, I just alsowant to have a history with the working directory.

这是我使用的单衬。把它贴在这里是因为它非常简单,而且我对每个会话的历史记录没有问题,我想拥有工作目录的历史记录。

Also the one-liner above mucks with your user interface too much.

此外,上面的单行代码过多地破坏了您的用户界面。

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

Since my home dir is typically a cross-mounted gluster thingy, this has the side effect of being a history of everything I've ever done. Optionally add $(hostname)to the echo command above... depending on your working environment.

由于我的家庭目录通常是一个交叉安装的 gluster 东西,这具有成为我做过的所有事情的历史的副作用。可以选择添加$(hostname)到上面的 echo 命令...取决于您的工作环境。

Even with 100k entries, grep is more than good enough. No need to sqlite log it. Just don't type passwords on the command line and you're good. Passwords are 90's tech anyway!

即使有 10 万个条目,grep 也足够好。无需sqlite记录它。只是不要在命令行上输入密码,你很好。无论如何,密码是 90 年代的技术!

Also, for searching I tend to do this:

另外,为了搜索,我倾向于这样做:

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

回答by spawn

Full disclosure: I'm the author of the FOSS-tool
shournal - A (file-) journal for your shell:
Using it's bash integration, the working directory of a command is also stored within shournal's sqlite-database and can be retrieved via

完全披露:我是 FOSS-tool
shournal - A (file-) journal for your shell 的作者
使用它的bash 集成,命令的工作目录也存储在 shournal 的 sqlite-database 中,可以通过检索

shournal --query -cmdcwd "$PWD"

Querying for sub-working-directories can be done with

查询子工作目录可以用

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

回答by Riaan

Gentleman this works better.. The only thing I can not figure out is how to make the script NOT log to syslog on login and log the last command in history. But works like a charm so far.

绅士这效果更好..我唯一想不通的是如何使脚本在登录时不记录到系统日志并记录历史记录中的最后一个命令。但到目前为止,它就像一个魅力。

#!/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  }' |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  }'`


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

}
export PROMPT_COMMAND='trackerbash'

回答by Chris Stryczynski

You could consider an independent project (I wrote it) that supports saving the path for each command: https://github.com/chrissound/MoscoviumOrange

你可以考虑一个独立的项目(我写的),支持保存每个命令的路径:https: //github.com/chrissound/MoscoviumOrange

Where you can add a hook into Bash to save each entry with:

您可以在 Bash 中添加一个钩子来保存每个条目:

$(jq -n --arg command "" --arg path "$PWD" '{"command":$command, "path":$path}' | "$(echo 'readlink -f $(which nc)' | nix run nixpkgs.netcat)" -N -U ~/.config/moscoviumOrange/monitor.soc &)