Ale*_*erg 11 command-history zsh oh-my-zsh macos
每隔一段时间我就会发现我的zsh
历史记录被截断了(或者可能完全丢失了,很难说),我必须从备份中恢复它。
例如,今天:
ls -lh ~/.zsh_history
-rw------- 1 stripe staff 32K 21 Feb 10:20 /Users/stripe/.zsh_history
Run Code Online (Sandbox Code Playgroud)
但是在我几天前的备份中:
-rw-------@ 1 stripe staff 203K 17 Feb 22:36 /Volumes/Time Machine Backups/.../Users/stripe/.zsh_history
Run Code Online (Sandbox Code Playgroud)
我已经配置zsh
为保存大量历史记录,所以这不应该是 shell 有意修剪文件的问题。
unsetopt share_history
setopt inc_append_history
setopt hist_ignore_all_dups
HISTSIZE=500000
SAVEHIST=$HISTSIZE
Run Code Online (Sandbox Code Playgroud)
有没有其他人经历过这种情况并找到了缓解它的方法?是否有一些zsh
我不知道的春季大扫除功能?
Eri*_*nil 13
我不认为任何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
Run Code Online (Sandbox Code Playgroud)
请随意使用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
Run Code Online (Sandbox Code Playgroud)
这来自这篇优秀的文章(“组合 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")]
}
Run Code Online (Sandbox Code Playgroud)
它工作正常,并返回一个有效的 zsh_history 文件,其中包含我的所有命令,并且比最大的备份大不了多少。
ZSH
由于多种原因,历史文件可能会被截断/丢失/清理,这些原因可能是:
$HOME
未定义 env 变量)笔记
历史可用设置选项
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.
Run Code Online (Sandbox Code Playgroud)
回答
对于这种情况,建议使用以下配置(在~/.zshrc
文件中设置)
HISTFILE=/specify/a/fixed/and/different/location/.history
HISTSIZE=500000
SAVEHIST=500000
setopt appendhistory
setopt INC_APPEND_HISTORY
setopt SHARE_HISTORY
Run Code Online (Sandbox Code Playgroud)
选择
您可以使用一个小脚本来检查历史文件大小并在必要时从备份中恢复它(在~/.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
Run Code Online (Sandbox Code Playgroud)
链接
我找到了替代解决方案:我安装了自定义命令历史记录工具。
我正在使用麦克飞。除了其用于更智能的历史搜索的简洁功能之外,在这种情况下的关键是,为了工作,该工具必须维护自己的历史数据库。
增强 shell 历史记录以跟踪 SQLite 数据库中的命令退出状态、时间戳和执行目录。
我的希望是,即使我的.zsh_history
数据库不断损坏,McFly 的 sqlite 数据库仍将作为后盾。
事实上,.zsh_history
出于兼容性原因,McFly 会自动更新,到目前为止,大约一个半月后,我还没有看到它丢失任何数据。sqlite 和.zsh_history
文件都在稳定增长。我.zsh_history
现在是 370K,我相信这是一个新记录。