将Zsh历史记录保存到〜/ .persistent_history

时间:2015-05-15 00:55:01

标签: bash shell zsh

最近我想在Mac上试用Z shell。但是我想继续将命令历史保存到〜/ .persistent_history,这就是我在Bash(ref)中所做的。

但是,参考链接中的脚本在Zsh下不起作用:

log_bash_persistent_history()
{
   [[
     $(history 1) =~ ^\ *[0-9]+\ +([^\ ]+\ [^\ ]+)\ +(.*)$
   ]]
   local date_part="${BASH_REMATCH[1]}"
   local command_part="${BASH_REMATCH[2]}"
   if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ]
   then
     echo $date_part "|" "$command_part" >> ~/.persistent_history
     export PERSISTENT_HISTORY_LAST="$command_part"
   fi
}
run_on_prompt_command()
{
   log_bash_persistent_history
}
PROMPT_COMMAND="run_on_prompt_command"

有没有人可以帮我搞定?非常感谢!

4 个答案:

答案 0 :(得分:2)

经过这么多谷歌搜索,我终于找到了做到这一点的方法。 首先,在〜/ .zshrc中,为历史操作添加以下选项:

setopt append_history # append rather then overwrite
setopt extended_history # save timestamp
setopt inc_append_history # add history immediately after typing a command

简而言之,这三个选项会立即将每个input_time +命令记录到〜/ .zsh_history。 然后,将此函数放入〜/ .zshrc:

precmd() { # This is a function that will be executed before every prompt
    local date_part="$(tail -1 ~/.zsh_history | cut -c 3-12)"
    local fmt_date="$(date -d @${date_part} +'%Y-%m-%d %H:%M:%S')"
    # For older version of command "date", comment the last line and uncomment the next line
    #local fmt_date="$(date -j -f '%s' ${date_part} +'%Y-%m-%d %H:%M:%S')"
    local command_part="$(tail -1 ~/.zsh_history | cut -c 16-)"
    if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ]
    then
        echo "${fmt_date} | ${command_part}"  >> ~/.persistent_history
        export PERSISTENT_HISTORY_LAST="$command_part"
    fi
}

由于我同时使用bash和zsh,所以我想要一个可以保存所有历史命令的文件。在这种情况下,我可以使用" grep"。

轻松搜索所有这些内容

答案 1 :(得分:1)

最初的答案大多是好的,但要处理多行命令,这些命令也包含字符':'例如,这有效:

local line_num_last=$(grep -ane '^:' ~/.zsh_history | tail -1 | cut -d':' -f1 | tr -d '\n')
local date_part="$(gawk "NR == $line_num_last {print;}" ~/.zsh_history | cut -c 3-12)"
local fmt_date="$(date -d @${date_part} +'%Y-%m-%d %H:%M:%S')"
local command_part="$(gawk "NR >= $line_num_last {print;}" ~/.zsh_history | sed -re '1s/.{15}//')"

答案 2 :(得分:1)

暂时无法评论(这超出了简单的修正),因此我将此作为答案添加。

例如,当最后一个命令花费相当多的时间来执行时,

This correctionthe accepted answer并不起作用 - 您将获得杂散数字{{1在你的命令中,像这样:

;

这可以通过将2017-07-22 19:02:42 | 3;micro ~/.zshrc && . ~/.zshrc 中的sed -re '1s/.{15}//'替换为稍长的command_part来解决,这也避免了我们的管道:

gawk

处理多行命令时也存在问题,其中一行以local command_part="$(gawk " NR == $line_num_last { pivot = match(\$0, \";\"); print substr(\$0, pivot+1); } NR > $line_num_last { print; }" ~/.zsh_history)" 开头。这可以(大部分)通过用:替换grep -ane '^:' ~/.zsh_history中的line_num_last来解决 - 我主要是因为命令可以包含匹配该表达式的字符串。说,

grep -anE '^: [0-9]{10}:[0-9]*?;' ~/.zsh_history

这将导致% naughty "multiline > command > : 0123456789:123;but a command I'm not > " 中的破坏记录。

为了解决这个问题,我们需要检查前一个redord是否以~/.persistent_history结尾(可能还有其他条件但我还不熟悉此历史记录格式),如果是这样的话< em>尝试上一场比赛。

\

答案 3 :(得分:0)

如果您希望能够为 bash 和 zsh 添加持久历史记录,请尝试以下操作:

# You should source this file from both .zshrc and .bashrc

if [ -n "${ZSH_VERSION}" ]; then
    setopt append_history # append rather then overwrite
    setopt extended_history # save timestamp
    setopt inc_append_history # add history immediately after typing a command

    _get_line_num_last () {
      local attempts=0
      local line=0
      while true; do
        # Greps the last two lines that can be considered history records
        local lines="$(grep -anE '^: [0-9]{10}:[0-9]*?;' ~/.zsh_history | \
                     tail -n $((2 + attempts)) | head -2)"
        local previous_line="$(echo "$lines" | head -1)"
        # Gets the line number of the line being tested
        local line_attempt=$(echo "$lines" | tail -1 | cut -d':' -f1 | tr -d '\n')
        # If the previous (possible) history records ends with `\`, then the
        # _current_ one is part of a multiline command; try again.
        # Probably. Unless it was in turn in the middle of a multi-line
        # command. And that's why the last line should be saved.
        if [[ $line_attempt -ne $HISTORY_LAST_LINE ]] && \
           [[ $previous_line == *"\\" ]] && [[ $attempts -eq 0 ]];
        then
          ((attempts+=1))
        else
          line=$line_attempt
          break
        fi
      done
      echo "$line"
    }

    precmd() {
      local line_num_last="$(_get_line_num_last)"
      local date_part="$(awk "NR == $line_num_last {print;}" ~/.zsh_history | cut -c 3-12)"
      # Try to get date with non-mac date function.
      local fmt_date="$(date -d @${date_part} +'%Y-%m-%d %H:%M:%S')" >& /dev/null
      # Try again with mac date function if that failed.
      if [ -z "$fmt_date" ]; then
          local fmt_date="$(date -r 1623959079 +'%Y-%m-%d %H:%M:%S')" >& /dev/null
      fi
      # I use awk itself to split the _first_ line only at the first `;`
      local command_part="$(awk "
        NR == $line_num_last {
          pivot = match(\$0, \";\");
          print substr(\$0, pivot+1);
        }
        NR > $line_num_last {
          print;
        }" ~/.zsh_history)"
      if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ]
      then
        echo "${fmt_date} | ${command_part}" >> ~/.persistent_history
        export PERSISTENT_HISTORY_LAST="$command_part"
        export HISTORY_LAST_LINE=$((1 + $(wc -l < ~/.zsh_history)))
      fi
    }
elif [ -n "${BASH_VERSION}" ]; then
    log_bash_persistent_history()
    {
      [[
        $(history 1) =~ ^\ *[0-9]+\ +([^\ ]+\ [^\ ]+)\ +(.*)$
      ]]
      local date_part="${BASH_REMATCH[1]}"
      local command_part="${BASH_REMATCH[2]}"
      if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ]
      then
        echo $date_part "|" "$command_part" >> ~/.persistent_history
        export PERSISTENT_HISTORY_LAST="$command_part"
      fi
    }
    export PROMPT_COMMAND="log_bash_persistent_history"
fi

export HISTSIZE=1000000
export HISTFILESIZE=-1
export HISTCONTROL=ignoredups:erasedups
export HISTTIMEFORMAT="%F %T  "

alias persistent_history='cat ~/.persistent_history'
alias ph='cat ~/.persistent_history'
alias phgrep='ph | grep'
alias phg='ph | grep'