每隔一段时间,我就会发现我的zsh
历史记录被截断(或者可能完全丢失,很难说),我必须从备份中恢复它。
例如,今天:
ls -lh ~/.zsh_history
-rw------- 1 stripe staff 32K 21 Feb 10:20 /Users/stripe/.zsh_history
但在我几天前的备份中:
-rw-------@ 1 stripe staff 203K 17 Feb 22:36 /Volumes/Time Machine Backups/.../Users/stripe/.zsh_history
我已配置zsh
为保存大量历史记录,因此这不应该是 shell 故意修剪文件的问题。
unsetopt share_history
setopt inc_append_history
setopt hist_ignore_all_dups
HISTSIZE=500000
SAVEHIST=$HISTSIZE
有其他人经历过这种情况并找到减轻这种情况的方法吗?是否有一些zsh
我不知道的春季大扫除功能?
答案1
中兴
历史文件可能由于多种原因而被截断/丢失/清除,这些原因可能是:
- zsh 历史文件损坏(由于打开 shell 时断电/系统故障,在这种情况下,需要设置 fsck 以便在系统故障时运行)
- Zsh 配置文件未加载(例如,如果
$HOME
未定义 env 变量) - 不支持历史档案中的人物可以让zsh重置历史记录
- 清洁工具,如漂白剂
- Zsh 配置错误
- 在 shell 之间共享
$HISTFILE
具有更严格的限制$HISTSIZE
(甚至是隐式的,例如,如果您$HISTFILE
在 bashrc 中没有 a 的 zsh 下运行 bash shell,则子 shell 将使用从 zsh 继承的变量并将应用$HISTSIZE
bashrc 中定义的变量) - ETC。
笔记
历史可用设置选项
HISTFILE="$HOME/.zsh_history"
HISTSIZE=500000
SAVEHIST=500000
setopt BANG_HIST # Treat the '!' character specially during expansion.
setopt EXTENDED_HISTORY # Write the history file in the ":start:elapsed;command" format.
setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits.
setopt SHARE_HISTORY # Share history between all sessions.
setopt HIST_EXPIRE_DUPS_FIRST # Expire duplicate entries first when trimming history.
setopt HIST_IGNORE_DUPS # Don't record an entry that was just recorded again.
setopt HIST_IGNORE_ALL_DUPS # Delete old recorded entry if new entry is a duplicate.
setopt HIST_FIND_NO_DUPS # Do not display a line previously found.
setopt HIST_IGNORE_SPACE # Don't record an entry starting with a space.
setopt HIST_SAVE_NO_DUPS # Don't write duplicate entries in the history file.
setopt HIST_REDUCE_BLANKS # Remove superfluous blanks before recording entry.
setopt HIST_VERIFY # Don't execute immediately upon history expansion.
setopt HIST_BEEP # Beep when accessing nonexistent history.
回答
~/.zshrc
对于这种情况,建议使用以下配置(在文件中设置)
HISTFILE=/specify/a/fixed/and/different/location/.history
HISTSIZE=500000
SAVEHIST=500000
setopt appendhistory
setopt INC_APPEND_HISTORY
setopt SHARE_HISTORY
选择
您可以使用一个小脚本来检查历史文件大小并在必要时从备份中恢复它(在~/.zshrc
)
if [ /home/my/zsh/hist/file -lt 64000 ]; then
echo "History file is lower than 64 kbytes, restoring backup..."
cp -f /mybackup/histfile /home/my/zsh/hist/file
fi
链接
答案2
我不认为任何zsh 选项将拯救您宝贵的.zsh_history
.
我的.zsh_history
多年来一直被随机截断,但我仍然不知道为什么。我已经尝试了在 StackExchange 上可以找到的所有选项,并且显然尝试了 oh-my-zsh 中的配置。
自动备份
为了不关心下次我的历史记录被截断,我将这一行添加到crontab -e
:
30 14 * * * cp /home/my_user/.zsh_history /backup/folder/zsh_history_$(date +\%Y_\%m_\%d).bak
请随意使用anacron
或rsync
任何其他工具。目标是在安全的地方收集.zsh_history
文件,其中至少有一些包含所需的信息。
恢复历史记录
当你需要恢复时一要完成.zsh_history
可能被截断的备份,您可以使用以下命令:
cat zsh_history*.bak | awk -v date="WILL_NOT_APPEAR$(date +"%s")" '{if (sub(/\\$/,date)) printf "%s", $0; else print $0}' | LC_ALL=C sort -u | awk -v date="WILL_NOT_APPEAR$(date +"%s")" '{gsub('date',"\\\n"); print $0}' > .merged_zsh_history
都来自于这个优秀的文章(“组合 zsh 历史文件”)。
它合并历史文件,对命令进行排序,删除重复项并且不会破坏多行命令。
几周后
按照计划,我的内容.zsh_history
无缘无故地被截断了。
我的备份工作正常。其中一些在同一文件中仍然有重复的命令。上面的 awk 代码仅识别文件之间的精确重复项(时间+持续时间+命令),但如果例如ls
在不同时间调用,则会保留它们。所以我写了这个小 Ruby 脚本:
#! /usr/bin/env ruby
# Ruby script to merge zsh histories. In case of duplicates, it removes the old timestamps.
# It should do fine with multi-line commands.
# Make backups of your backups before running this script!
#
# ./merge_zsh_histories.rb zsh_history_*.bak ~/.zsh_history > merged_zsh_history
MULTILINE_COMMAND = "TO_BE_REMOVED_#{Time.now.to_i}"
commands = Hash.new([0,0])
ARGV.sort.each do |hist|
$stderr.puts "Parsing '#{hist}'"
content = File.read(hist)
content.scrub!("#")
content.gsub!(/\\\n(?!:\s*\d{10,})/, MULTILINE_COMMAND)
should_be_empty = content.each_line.grep_v(/^:/) + content.each_line.grep(/(?<!^): \d{10,}/)
raise "Problem with those lines : #{should_be_empty}" unless should_be_empty.empty?
content.each_line do |line|
description, command = line.split(';', 2)
_, time, duration = description.split(':').map(&:to_i)
old_time, _old_duration = commands[command]
if time > old_time
commands[command] = [time, duration]
end
end
end
commands.sort_by{|_, time_duration| time_duration}.each{|command, (time, duration)|
puts ':%11d:%d;%s' % [time, duration, command.gsub(MULTILINE_COMMAND, "\\\n")]
}
它工作正常,并返回一个有效的 zsh_history 文件,其中包含我的所有命令,并且比最大的备份大不了多少。
答案3
我找到了替代解决方案:我安装了自定义命令历史记录工具。
我在用着麦克弗莱。除了其用于更智能的历史搜索的简洁功能之外,在这种情况下的关键是,为了工作,该工具必须维护自己的历史数据库。
增强 shell 历史记录以跟踪 SQLite 数据库中的命令退出状态、时间戳和执行目录。
我的希望是,即使我的数据库.zsh_history
不断损坏,McFly 的 sqlite 数据库仍将作为后盾。
事实上,.zsh_history
出于兼容性原因,McFly 会自动更新,到目前为止,大约一个半月后,我还没有看到它丢失任何数据。 sqlite 和.zsh_history
文件都在稳定增长。我.zsh_history
现在是 370K,我相信这是一个新记录。
答案4
如果回复的话还不算太晚。当我的 zhistory 文件损坏时,总是由于突然关闭而导致。当我重新启动时,我只需编辑 ~/.zhistory 文件并删除明显损坏的行。通常是最后一行,但有时是倒数第二行。那我就没事了。我的 zhistory 文件将近 1meg,跨度很多年。