将 Zsh 历史保存到 ~/.persistent_history
Save Zsh history to ~/.persistent_history
最近想在Mac中尝试Zshell。但我还想继续将命令历史记录保存到 ~/.persistent_history,这是我在 Bash (ref).
中所做的
但是,ref link 中的脚本在 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"
有没有人可以帮助我让它工作?非常感谢!
在谷歌搜索了这么多之后,我终于找到了这样做的方法。
首先,在 ~/.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".
轻松搜索所有这些
原来的答案大部分都不错,但是要处理还包含字符“:”的多行命令,例如这样可行:
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}//')"
还不能发表评论(这超出了简单的更正范围),所以我将其添加为答案。
to 不太有效,例如,最后一个命令执行起来花费了相当多的时间 - 你会在你的命令中得到杂散的数字和 ;
,就像这样:
2017-07-22 19:02:42 | 3;micro ~/.zshrc && . ~/.zshrc
这可以通过将 command_part
中的 sed -re '1s/.{15}//'
替换为稍长的 gawk
来解决,这也避免了我们使用管道:
local command_part="$(gawk "
NR == $line_num_last {
pivot = match($0, \";\");
print substr($0, pivot+1);
}
NR > $line_num_last {
print;
}" ~/.zsh_history)"
在处理其中一行以 :
开头的多行命令时也会出现问题。这可以(大部分)通过将 line_num_last
中的 grep -ane '^:' ~/.zsh_history
替换为 grep -anE '^: [0-9]{10}:[0-9]*?;' ~/.zsh_history
来解决 - 我说主要是因为命令可以想象包含匹配该表达式的字符串。说,
% naughty "multiline
> command
> : 0123456789:123;but a command I'm not
> "
这将导致 ~/.persistent_history
中的记录被破坏。
为了解决这个问题,我们需要依次检查以前的记录是否以 \
结尾(可能还有其他情况,但我还不熟悉这种历史记录格式),如果是 尝试上一场比赛。
_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="$(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')"
# I use awk itself to split the _first_ line only at the first `;`
local command_part="$(gawk "
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
}
如果您希望能够为 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'
最近想在Mac中尝试Zshell。但我还想继续将命令历史记录保存到 ~/.persistent_history,这是我在 Bash (ref).
中所做的但是,ref link 中的脚本在 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"
有没有人可以帮助我让它工作?非常感谢!
在谷歌搜索了这么多之后,我终于找到了这样做的方法。 首先,在 ~/.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".
轻松搜索所有这些原来的答案大部分都不错,但是要处理还包含字符“:”的多行命令,例如这样可行:
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}//')"
还不能发表评论(这超出了简单的更正范围),所以我将其添加为答案。
;
,就像这样:
2017-07-22 19:02:42 | 3;micro ~/.zshrc && . ~/.zshrc
这可以通过将 command_part
中的 sed -re '1s/.{15}//'
替换为稍长的 gawk
来解决,这也避免了我们使用管道:
local command_part="$(gawk "
NR == $line_num_last {
pivot = match($0, \";\");
print substr($0, pivot+1);
}
NR > $line_num_last {
print;
}" ~/.zsh_history)"
在处理其中一行以 :
开头的多行命令时也会出现问题。这可以(大部分)通过将 line_num_last
中的 grep -ane '^:' ~/.zsh_history
替换为 grep -anE '^: [0-9]{10}:[0-9]*?;' ~/.zsh_history
来解决 - 我说主要是因为命令可以想象包含匹配该表达式的字符串。说,
% naughty "multiline
> command
> : 0123456789:123;but a command I'm not
> "
这将导致 ~/.persistent_history
中的记录被破坏。
为了解决这个问题,我们需要依次检查以前的记录是否以 \
结尾(可能还有其他情况,但我还不熟悉这种历史记录格式),如果是 尝试上一场比赛。
_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="$(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')"
# I use awk itself to split the _first_ line only at the first `;`
local command_part="$(gawk "
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
}
如果您希望能够为 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'