最近我一直在使用一台安装了 MiKTeX 的 W eird indows 机器。通过它的自动软件包安装程序,我注意到,如果我检查一个我确信没有安装的软件包文件是否存在:
\documentclass{article}
\begin{document}
\IfFileExists{abntex2.cls}{Oh, no!}{Phew :)}
\end{document}
MikTeX 将安装那包,测试将返回 true。当然,这是完全合理的(安装,而不是包),因为\IfFileExists
打开文件进行读取以测试其存在,一旦打开文件,MiKTeX 就会尝试安装它。
为了避免这种情况,我可以使用--disable-installer
选项,不出所料,该选项将禁用自动软件包安装程序。但是我想知道是否有(或者我是否可以制作)一个宏级接口,允许我检查软件包是否已安装或是否是安装候选,而不会触发软件包的自动安装。最好不管软件包管理器的当前设置如何。可能吗?
答案1
在这个答案中,我将假设一个 Lua 支持引擎(LuaTeX、HarfTeX 等)
MiKTeX 非常渴望安装软件包,所以你必须欺骗它:如果你正在寻找的文件已经存在于当前目录中,MiKTeX 会很高兴,并且当你向它询问该文件时,它不会安装该软件包。
计划是这样的:在当前工作目录中放置一个虚拟文件。然后通过 kpathsea 模拟询问 MiKTeX全部已知与名称匹配的文件。当然第一个将是本地虚拟文件,但如果找到另一个,则表示已安装该包。然后最后删除虚拟文件。
\documentclass{article}
\newluafunction\filereallyexistsfuncid
\directlua{
local truetok, falsetok = token.create'@firstoftwo', token.create'@secondoftwo'
lua.get_functions_table()[\the\filereallyexistsfuncid] = function()
local filename = token.scan_string()
local f = io.open(filename) % First test if the file exists in the current folder
if f then
f:close()
return token.put_next(truetok) % In this case we are done, the file exists
end
f = io.open(filename, "w") f:close() % Otherwise create it to stop MiKTeX from installing anything
local _, f2 = kpse.lookup(filename,{all=true}) % Now we will always find the local file first and MiKTeX is happy. If the file existed before, it will be found too.
os.remove(filename) % The local file did it's job, delete it
token.put_next(f2 and truetok or falsetok) % And write the answer to TeX
end
}
\newcommand\filereallyexists{\luafunction\filereallyexistsfuncid}
\begin{document}
\filereallyexists{hyperbar.sty}{AWESOME!}{How can you be so cruel?}
\end{document}
这里需要注意 Lua 语言中比较特殊的结构:
kpse.lookup
如果 ,则对找到的每个文件返回all=true
一个返回值。因此,如果只找到一个文件,则只返回一个值。如果找到两个文件,则返回两个值……该行
local _, f2 = kpse.lookup(...)
将前两个返回值存储在变量_
和 中f2
。(在 Lua 中_
是一个常规变量,但按照惯例,_
如果要忽略某个值,则使用 )存储在那里的实际值是找到的文件的完整绝对文件名。但如果只找到一个文件会发生什么?那么就没有“前两个返回值”,只返回一个文件名。在这种情况下,Lua 将其余变量设置为特殊值nil
。
在我们的代码中,唯一重要的问题是:是f2
字符串还是nil
?在 Lua 中,每一个字符串(包括空字符串""
)被视为真,但nil
被视为false
。所以我们可以将其视为f2
布尔值。这将我们带到最后一行:
token.put_next(f2 and truetok or falsetok)
是a and b or c
Luaa ? b : c
中与 C 语言中的三级条件运算符等价的运算符:如果a
(在我们的例子中f2
)为true
,则得到b
(truetok
),否则得到c
(falsetok
)。只要b
永远不会false
(或)作为 Lua 的与的nil
短路的结果,此运算符就有效:我们有被视为。如果被视为,则当且仅当。因此 Lua 只需评估为and
or
a and b or c
(a and b) or c
a
true
a and b
b
a and b
b
无需将其转换为布尔值。我们假设b
为真,所以(a and b) or c === b or c
始终为true
,甚至c
不必进行求值,因此 Lua 只需返回b
,无需转换为布尔值。因此 Lua 的and
和or
总是返回确定其真值的第一个参数。(对于值会发生什么,false
留给a
读者练习 ;-))