如果你使用 KDE + archlinux,那么很有可能会遇到这个烦人的问题:WPS 打开文件的方式不对。
问题表现为,在 KDE 上试图单击打开 .xls 文件的时候,WPS Writer 会被调用,并且很有可能卡死(因为文件内容不匹配)。如果你尝试给 .xls 设置默认程序,会发现可能 .doc, .docx, .ppt, .pptx 也被影响。并且这类文件均被识别成 x-ole-storage 而非独立的 Presentation, Word, Spreedsheet 类型。
aur 源里有一个叫 wps-office-mime 的包,安装这个包不能解决问题。
这件事困扰了我很久了,非常烦人,一不小心单击打开表格文件,就会调用 WPS Writer 然后卡死。之前启用 WPS 的整合模式可以解决这个问题,但是自从某个更新后整合模式会崩溃。
有一天,我再也忍不了了,下定决心要把这件事调查清楚……
首先解释一个名词,mimetype,简单来说就是“判断一个文件是什么类型,应该用什么程序打开”的规则。
前面提到,很多不同的文档文件( doc, ppt, xls) 都被匹配成 x-ole-storage
了,而一个类型的文件只有一个默认打开程序,因此要关联就都关联到 WPS Writer 上从而导致错误结果。
问题:我的 .xlsx 文件是怎么被识别成 x-ole-storage 文件并打开的?
我打开 KDE 的 File Associations,里面确实有 .ppt, .doc, .xls 的 mimetypes 及其关联的打开程序,看起来非常正常。但是为什么这些规则没有匹配到我的文件上呢?为什么文件没有被正确归类成幻灯片,文档,表格,而是那个不知哪里来的 x-ole-storage 类型?
ArchWiki 有关于 mimetypes 的一些介绍。其中提到系统直接读取的 mimetypes 其实是一种数据库,或者说缓存,是根据安装过的软件包的原始 mimetype 配置文件生成的结果。
原始 mimetype 配置文件储存在:
/usr/share/mime/packages
~/.local/share/mime/packages
并且我们可以用 update-mime-database
来根据配置刷新缓存。
好吧,可是这些知识对于解决我的问题并没有帮助。
不过,至少经过在这些目录里搜索,我发现 x-ole-storage
的定义来源于 /usr/share/mime/packages/freedesktop.xml
,属于 XDG 标准内容,看来我不应该删除掉它的定义。
看来需要调查 KDE 识别并打开文件的过程了。
根据日常使用 KDE 的经验和猜想,在 Dolphin 里单击一个文件,和使用 xdg-open
命令的效果是完全相同的。经过一些搜索,我发现 xdg-open
依赖 kde-cli-tools
包来打开文件,那么想要调查 KDE 打开文件,自然要去看这个包里都有什么工具了。
找到其源码,看一下目录结构初步判断确实和 mimetype 有关。浏览源码的时候发现一个工具 kmimetypefinder5,这部分代码看起来对于追踪 KDE 处理 mimetype 的流程非常有用!我在终端调用 kmimetypefinder5 somefile.xlsx
,发现返回结果是 x-ole-stroage
,我复现了想要的错误。接下来就是看这个工具的源码,一点一点找在哪里 mimetype 没有正确匹配了。
还好这个工具的源码很简单,我很快发现它是调用 Qt 库中的 QMimeDatabase
来判断 mimetype 的。
所以只能看看 Qt 库在干什么了。首先读了一下对应 Qt 文档,发现只说明了提供的接口,没有介绍判断逻辑。
那么接下来只能读源码了。
Google "QMimeDatabase source" 很快定位到了我想要的东西
QMimeType QMimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *accuracyPtr)
{
// First, glob patterns are evaluated. If there is a match with max weight,
// this one is selected and we are done. Otherwise, the file contents are
// evaluated and the match with the highest value (either a magic priority or
// a glob pattern weight) is selected. Matching starts from max level (most
// specific) in both cases, even when there is already a suffix matching candidate.
所以说,KDE 经过一系列调用之后,最终调用 Qt 库中这个函数来获取一个文件的 mimetype, 然后用关联的默认程序来打开文件。
简单阅读注释 + 代码,发现 Qt 是根据文件的扩展名和 Magic Number 共同启发式推断文件的类型。也就是说,如果有多个匹配,Qt 会给这些匹配结果分配权重,然后选择权重最高的匹配结果。
没看多远,我就发现了一条我最想要的匹配规则:如果根据文件扩展名的规则匹配,有唯一的匹配结果,那么立刻返回这个匹配。
// Pass 1) Try to match on the file name
QMimeGlobMatchResult candidatesByName;
if (fileName.endsWith(QLatin1Char('/')))
candidatesByName.addMatch(QLatin1String("inode/directory"), 100, QString());
else
candidatesByName = findByFileName(QFileInfo(fileName).fileName());
if (candidatesByName.m_allMatchingMimeTypes.count() == 1) {
*accuracyPtr = 100;
const QMimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0));
if (mime.isValid())
return mime;
candidatesByName = {};
}
看到这里,不需要阅读后面的源码,谜底也已经解开了,解决方案已经有了。让我的系统里对于 .doc 等文件只有一种匹配。
打开 KDE File Associations, 把 WPS 加进来的乱七八糟的 mimetypes 删干净,只留下 XDG 标准定义的文档类型,并且为其设置正确的独立程序,如 *.ppt 默认使用 WPS Presentation 而非 WPS 2019 打开。
测试一下,终于成了!我终于能在 Linux 下不糟心的阅读 Office 文档了!对,我们用 Linux 的就是会糟心这种问题……
一句话解决方案:在系统设置里清理 mimetype,把重复匹配 .doc/.docx/.ppt/.pptx 等等的 mimetype 删的只剩下一个,保证每个后缀名只在一个 mimetype 里出现一次。
如果系统设置里删不掉,就找上文提到的“原始 mimetype 配置文件储存路径”
/usr/share/mime/packages
~/.local/share/mime/packages
并用合适的参数调用 update-mime-database
相信被这个问题困扰的 Linux 用户不少,希望能帮到你 :)
感谢阅读
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.