记一次ext4文件系统损坏,zsh corrupt history file

zsh: corrupt history file /home/zhixi/.histfile

Vmware虚拟机里面的那台arch因为用的ext4,然后经常随手强行关机就坏了。

已经挂载上了fsck不太好修。

1
2
3
4
fsck from util-linux 2.40
e2fsck 1.47.0 (5-Feb-2023)
/dev/sda2 is mounted.
e2fsck: Cannot continue, aborting.

要用fsck需要,改设置CD-ROM启动,然后chroot挂载,然后fsck

但是zsh history文件比较简单,直接修复一下就可以了。网上有很多教程,比如

1
2
3
4
5
cd ~
mv .zsh_history .zsh_history_bad
strings .zsh_history_bad > .zsh_history
fc -R .zsh_history
rm ~/.zsh_history_bad

我把zsh的历史命令设置存到~/histfile了,所以应该是

1
2
3
4
5
mv ~/.histfile ~/.histfile_bad
# 用strings命令提取
strings ~/.histfile_bad > ~/.histfile
fc -R ~/.histfile
rm ~/.histfile_bad

但是这样会有一个问题,中文乱码。即使使用strings -eS .zsh_history,能读取到中文命令,中文也会乱码。像下面这样

1
code 练�僰��总僲��-⃥�.go

试过几种编码都不行,查了一下发现zsh有自己的编码格式。可以这样解码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define Meta ((char) 0x83)

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

/* from zsh utils.c */
char *unmetafy(char *s, int *len)
{
char *p, *t;

for (p = s; *p && *p != Meta; p++);
for (t = p; (*t = *p++);)
if (*t++ == Meta)
t[-1] = *p++ ^ 32;
if (len)
*len = t - s;
return s;
}

int main(int argc, char *argv[]) {
char *line = NULL;
size_t size;

while (getline(&line, &size, stdin) != -1) {
unmetafy(line, NULL);
printf("%s", line);
}

if (line) free(line);
return EXIT_SUCCESS;
}

又查了一下在终端中可以直接用fc -l查看,fc 1 1000查看1到1000条的记录。那就好办了。$HISTFILE变量为历史存储的位置,SAVEHIST变量为保存历史的条数。

所以可以写成这样:

1
2
3
4
5
6
7
8
9
10
cp `$HISTFILE` ~/.histfile_bad
# 用strings命令提取会乱码
# strings `$HISTFILE` > ~/.histfile
# 用zsh内置fc命令自带的解码可以读取
fc -l 1 $SAVEHIST | awk '{print $2}' > ~/.histfile_tmp
cp ~/.histfile_tmp $HISTFILE
fc -R $HISTFILE
rm ~/.histfile_bad ~/.histfile_tmp
# 运行zsh没有报错就可以了
zsh

链接

How to fix a corrupt zsh history file
Re: Fw: ZSH history file VS. UTF-8 data