DC - Lua 脚本

# 2.15. Lua 脚本

## 目录

1. 简介
2. 需要的 DLL
3. Double Commander 函数库
  3.1. DC 库
    3.1.1. 使用 DC.ExecuteCommand 的示例
  3.2. 系统库
    3.2.1. SysUtils.FileGetAttr 返回值详解
    3.2.2. SysUtils.FileGetAttr 示例
    3.2.3. 使用 FindFirst、FindNext 与 FindClose 的示例
  3.3. 剪贴板库
    3.3.1. 剪贴板库 使用示例
  3.4. 对话框库
    3.4.1. Dialogs.MessageBox 中显示的按钮
    3.4.2. Dialogs.MessageBox 的窗口样式
    3.4.3. Dialogs.MessageBox 的默认按钮
    3.4.4. Dialogs.MessageBox 的返回值
    3.4.5. Dialogs.MessageBox 使用示例
    3.4.6. Dialogs.InputQuery 使用示例
  3.5. UTF-8 库
  3.6. 字符库
  3.7. 操作系统库
4. 索引

## 1. 简介

有关 Lua 脚本语言的详细信息，请参阅 Lua 官方网站。

Double Commander 可以通过 cm_ExecuteScript 命令执行 Lua 脚本。
脚本参数必须原样传递，不要进行转义（不要加引号或 "\"），为此需要使用 %"0 变量：例如，用 `%"0%p0` 表示光标下的文件（而非 `%p0`），或用 `%"0%D` 表示当前目录（而非 `%D`）。否则如果 Double Commander 自动添加了引号，引号会作为参数的一部分被传入，脚本中需考虑这一点。
要获取所有选中文件的列表，可以使用 变量（`%LU`、`%FU` 或 `%RU`），也可以使用内部命令（cm_SaveSelectionToFile、cm_SaveFileDetailsToFile、cm_CopyFullNamesToClip 或 cm_CopyFileDetailsToClip）。 例如可以使用 `%p`：这种情况下，Double Commander 会将所有选中的文件的名称放在同一行，并用空格分隔。

也可以使用 Lua 编写内容插件（content plugins），示例位于程序目录的 `plugins/wdx/scripts` 下。 Wiki 有一页专门简介插件编写：插件开发。 限制：仅支持以下数据类型：

- ft_numeric_32 (1) – 32 位有符号数；
- ft_numeric_64 (2) – 64 位有符号数；
- ft_numeric_floating (3) – 浮点数；
- ft_boolean (6) – 布尔类型：true 或 false；
- ft_multiplechoice (7) – 允许有限选择的值；
- ft_string (8) – 文本字符串；
- ft_fulltext (9) – 全文（多个文本字符串），仅用于插件搜索；
- ft_datetime (10) – 用于日期/时间：返回的日期将被转换为格式化的日期和时间字符串（值取决于您的区域设置）。
  日期必须为 Windows 格式，但 Lua 以及 Double Commander 提供的文件搜索功能使用 Unix 时间格式。要转换，我们可以使用以下公式：
  = UnixTime * 10000000 + 116444736000000000
  这是一个大数字，因此您需要 64 位版本的 Double Commander 或 Lua 5.3 以上版本。

以上列表包含头文件中的名称，在 Lua 脚本中，我们必须使用括号中指定的数值。

关于文本编码

下面描述的所有附加函数都接受 UTF-8 编码的字符串参数，并以 UTF-8 编码返回字符串（LazUtf8.ConvertEncoding 除外）。

部分标准 Lua 库函数已由 Double Commander 或 Free Pascal/Lazarus 的函数替换（或新增实现），以提供对 UTF-8 的支持。

编写插件时，也应对文本数据（ft_multiplechoice、ft_string 和 ft_fulltext）使用 UTF-8 编码。

保存脚本时请使用不带 BOM 的 UTF-8 编码。

注意事项

使用 Lua 自动化功能可以完成很多事情，但也有一些需要注意的细节，下面收集了一些常见的注意点。

1. 如果启用了自动刷新和在单独线程中加载文件列表选项，则刷新会以异步方式执行。而脚本在 Double Commander 的主线程中运行，因此在某些情况下可能会影响脚本的行为。例如，有时顺序执行的导航命令可能不起作用（例如目录很大或磁盘很慢），遇到这种情况可以尝试禁用“在单独线程加载文件列表”或寻找替代方案。

如果脚本在当前面板创建了新文件或重命名了已有文件，但脚本没有结束而继续执行其他操作（例如选择文件或移动光标），这些后续操作有时不会生效：因为面板中的文件列表尚未全部更新，此时需要先调用cm_Refresh命令。在上述情况下，`cm_Refresh` 也可能是异步执行的，Double Commander 可能无法在你的后续操作执行前完成刷新。

自动刷新与在独立线程加载文件列表是文件管理器中很实用的功能。实践中发现一种比较稳妥的方法是短暂让出控制权，等待文件列表完成刷新，示例如下：

    DC.ExecuteCommand("cm_Refresh")
    i = 10
    while i > 0 do
      SysUtils.Sleep(10)
      DC.ExecuteCommand("")
      i = i - 1
    end

2. Lua 的 `io.open` 使用标准 C 函数 `fopen`：在文本模式下，该函数在读写时可能会转换行结束符（CRLF、LF 或 CR），这会导致意外结果。如果遇到具有不同类型行结束符的文件或编写跨平台脚本时，应注意此点，通常更稳妥的做法是使用二进制模式。

3. 在 Linux 和其它类 Unix 系统中，用于 显示文件属性... 对话框的 `ContentGetValue` 函数会以 `CONTENT_DELAYIFSLOW` 标志（第四个参数，值为 1）调用，这样可以避免窗口打开时的长时间等待：如果数据检索较慢，可以通过检查该标志并为这些字段或插件返回 `nil` 来排除它们。

4. 如果插件需要返回空字符串，传递 `nil` 通常比传递 `""` 更快。

（返回顶部）

## 2. 需要的 DLL

要解释并运行 Lua 脚本，系统需要安装相应的 Lua 动态链接库（DLL / shared library），Double Commander 支持 Lua 5.1 至 5.4。

可以使用来自 LuaJIT 项目 的库文件。LuaJIT 提供高性能的解释器与 JIT 编译器，并包含 FFI 库，允许从纯 Lua 代码调用外部 C 函数和使用 C 数据结构。

Windows 版的 DC 发行包通常默认包含 Lua DLL（从 DC 0.9.7 起采用 LuaJIT），其他情况下可以通过包管理器安装或自行编译。如果使用 64 位的 DC，则需要对应的 64 位 Lua 库。

默认情况下，DC 在其程序目录和系统目录中查找名为 `lua5.1.dll`（Windows）、`liblua5.1.so.0`（Unix / Linux）或 `liblua5.1.dylib`（macOS）的文件。也可以在配置中通过 要使用的 Lua 库文件 参数更改库文件名或路径。

（返回顶部）

## 3. Double Commander 函数库

Double Commander 为 Lua 脚本提供了若干函数库。

下面列出这些库：

库列表
库名 | 脚本名 | 简要说明
--- | --- | ---
Double Commander 库 | DC | Double Commander 特有的函数
系统库 | SysUtils | 各种系统相关函数
剪贴板库 | Clipbrd | 提供与系统剪贴板交互的功能
对话框库 | Dialogs | 与用户交互（消息、输入等）
UTF-8 库 | LazUtf8 | UTF-8 字符串相关函数
字符库 | Char | 获取字符的 Unicode 类别与相关检查
操作系统库 | os | 与操作系统相关的函数

（返回顶部）

## 3.1. DC 库

该库包含 Double Commander 特有的函数。

所有函数都包装在 `DC` 表中。

DC 库
函数名 | 说明
--- | ---
DC.LogWrite | DC.LogWrite(sMessage, iMsgType, bForce, bLogFile) 向日志窗口写入一条消息： sMessage : 要写入日志的消息文本。 iMsgType : 消息类型：0 - 信息，1 - 成功，2 - 错误。 bForce : 布尔值，若为 true，则在日志窗口不可见时强制显示该窗口。 bLogFile : 布尔值，若为 true，则同时将消息写入日志文件。 Double Commander 用于写入日志的内部函数采用异步工作方式（请参阅简介中的说明），因此通过 `DC.LogWrite` 写入的消息不会立即记录到日志中，而是在脚本运行结束后才写入。若需要立即写入日志消息，可在 `DC.LogWrite` 后尝试调用 `DC.ExecuteCommand("")` 来实现。
DC.CurrentPanel | iPanel = DC.CurrentPanel() 获取活动面板：如果左侧面板是活动的返回 0，右侧返回 1。 DC.CurrentPanel(iPanel) 设置活动面板：当 iPanel 为 0 时切换到左侧面板，为 1 时切换到右侧面板。
DC.ExecuteCommand | DC.ExecuteCommand(sCommand, Param1, Param2,...,ParamX) 允许脚本调用 Double Commander 的 内部命令。 sCommand 为内部命令的名称。 可以按需提供多个 Param... 参数，具体取决于命令支持的参数数量。
DC.GoToFile | DC.GoToFile(sFileName, bActive) 打开 sFileName 文件所在的目录并将光标定位到 sFileName 文件。要打开目录，请使用 cm_ChangeDir 命令，或尝试在 sFileName 后添加目录分隔符和".."（不含引号）。 bActive —— 此为可选参数：函数可在活动文件面板（`true`）或非活动面板（`false`）中运行。默认值为`true`。
DC.GetPluginField | Result = DC.GetPluginField(sFileName, sPlugin, iField, iUnit) 允许通过已安装的信息（或内容）插件（WDX）获取数据： sFileName：文件的完整（绝对）名称。 sPlugin：插件名称，需与对应 插件配置 中保存的名称一致。 iField：插件字段索引（从0开始计数）。 iUnit：计量单位索引（从0开始计数）；如果不存在请指定0。 Result：返回值：可返回有符号整数（包括Unix时间格式）、浮点数、布尔值、字符串或 `nil` 。
DC.ExpandVar | sResult = DC.ExpandVar(String) 返回将其中所有变量替换为其对应值的字符串（环境变量除外，处理环境变量请使用SysUtils.ExpandEnv）。

除了内部命令外，脚本中还可以使用特殊命令 `cm_ExecuteToolBarItem`，该命令允许通过标识符调用工具栏按钮（程序内可用于给工具栏按钮绑定快捷键）。该命令的用法与一般内部命令类似（见下例），参数如下：

参数 | 取值 | 说明
--- | --- | ---
ToolBarID | TfrmOptionsToolbar | 主工具栏上的按钮
TfrmOptionsToolbarMiddle | 中间工具栏上的按钮
(缺省) | 主工具栏上的按钮（当未指定时）
ToolItemID | 标识符 | 按钮的唯一标识符

按钮的唯一标识符存储在 `ID` 标签中。获取方法包括：在 `doublecmd.xml` 中查找、查看工具栏备份文件，或将按钮复制到剪贴板然后粘贴到文本编辑器以读取其代码。

注意：标识符通常由程序自动生成，不需要与另一台程序实例中的相似按钮保持一致；如有需要也可以手动设置自定义标识符。

（返回顶部）

## 3.1.1. 使用 DC.ExecuteCommand 的示例

下面示例脚本执行以下操作：

1. 将焦点切换到右侧面板
2. 关闭所有打开的标签
3. 切换到指定文件夹
4. 将焦点切换到左侧面板
5. 关闭所有打开的标签
6. 切换到指定文件夹
7. 打开一个新标签
8. 切换到指定文件夹

    -- 1. 将焦点切换到右侧面板。
    DC.ExecuteCommand("cm_FocusSwap", "side=right")

    -- 2. 关闭所有打开的标签。
    DC.ExecuteCommand("cm_CloseAllTabs")

    -- 3. 切换到指定文件夹。
    DC.ExecuteCommand("cm_ChangeDir", "C:\\Users\\user0\\Music")

    -- 4. 将焦点切换到左侧面板。
    DC.ExecuteCommand("cm_FocusSwap", "side=left")

    -- 5. 关闭所有打开的标签。
    DC.ExecuteCommand("cm_CloseAllTabs")

    -- 6. 切换到指定文件夹。
    DC.ExecuteCommand("cm_ChangeDir", "C:\\Users\\Public\\Music")

    -- 7. 打开一个新标签。
    DC.ExecuteCommand("cm_NewTab")

    -- 8. 切换到指定文件夹。
    DC.ExecuteCommand("cm_ChangeDir", "C:\\ShareFolder")

可以使用内部命令 cm_ExecuteScript 在工具栏上配置一个按钮来执行脚本。

假设脚本文件为 `C:\scripts\lua\music.lua`，可以将按钮按如下方式配置：

此外，可以使用 Double Commander 内置的编辑器来编辑脚本。若文件名以 `.lua` 结尾，内置编辑器会识别并提供 Lua 语言的语法高亮：

（返回顶部）

## 3.2. 系统库

该库包含各种系统相关函数。

所有函数都包装在 `SysUtils` 表中。

系统库
函数名 | 说明
--- | ---
SysUtils.Sleep | SysUtils.Sleep(iMilliseconds) 暂停脚本执行指定的 iMilliseconds 毫秒。 到期后继续执行脚本。
SysUtils.GetTickCount | SysUtils.GetTickCount() 返回递增的时钟滴答计数，可用于时间测量，但不应对滴答间隔作严格假设。
SysUtils.FileExists | bExists = SysUtils.FileExists(sFileName) 检查指定文件是否存在于文件系统中。 如果磁盘上存在名为 sFileName 的文件，则在 bExists 中返回 `true`，否则返回 `false`。
SysUtils.DirectoryExists | bExists = SysUtils.DirectoryExists(sDirectory) 检查 sDirectory 是否存在且确为目录。 若是则返回 bExists 为 `true`，否则返回 `false`。
SysUtils.FileGetAttr | iAttr = SysUtils.FileGetAttr(sFileName) 在 iAttr 中返回文件 sFileName 的属性设置。 有关返回值的详细说明见 此处。
SysUtils.FindFirst | Handle, FindData = SysUtils.FindFirst(sPath) 查找匹配 sPath（通常包含通配符）的文件。 如果未找到任何文件，Handle 将为 `nil`。 如果找到至少一个项目，返回的 Handle 可用于随后的 `SysUtils.FindNext` 来查找同一模式的其他匹配项。 FindData 表包含有关找到的文件或目录的信息。 FindData 表的字段包括： Name ：文件名（不含路径）。 Attr ：文件属性（详见 此处）。 Size ：文件大小（字节）。 Time ：文件时间戳（自 1970 年 1 月 1 日起的秒数）。
SysUtils.FindNext | Result, FindData = SysUtils.FindNext(Handle) 使用之前返回的 Handle 查找由 `FindFirst` 发起的搜索序列的下一个匹配项。 如果找到文件或目录，返回的 Result 将非 nil，否则为 `nil`。 关于 `SysUtils.FindFirst` 的说明同样适用于此函数。 注意： 每次最后的 `SysUtils.FindNext` 调用必须配对一次 `SysUtils.FindClose`（传入相同的 Handle），否则会造成内存泄漏。
SysUtils.FindClose | SysUtils.FindClose(Handle) 结束一系列 `SysUtils.FindFirst`/`SysUtils.FindNext` 调用。 释放这些调用占用的内存。 务必调用此函数，否则可能会发生内存泄漏。
SysUtils.CreateDirectory | bResult = SysUtils.CreateDirectory(sDirectory) 创建目录链，sDirectory 为目标目录的完整路径。 如果目录已存在或创建成功则返回 `true`，如任一部分创建失败则返回 `false`。
SysUtils.RemoveDirectory | bResult = SysUtils.RemoveDirectory(sDirectory) 删除名为 sDirectory 的目录。成功时返回 `true` ，否则返回 `false` 。 与 os.remove 不同，此函数在出错时不会返回错误描述，但能够删除非空目录。
SysUtils.CreateHardLink | bResult = SysUtils.CreateHardLink(sFileName, sLinkName) 为文件 sFileName 创建硬链接 sLinkName。 成功返回 `true`，失败返回 `false`。
SysUtils.CreateSymbolicLink | bResult = SysUtils.CreateSymbolicLink(sFileName, sLinkName) 为文件或目录 sFileName 创建符号链接 sLinkName。 成功返回 `true`，失败返回 `false`。
SysUtils.ReadSymbolicLink | sTarget = SysUtils.ReadSymbolicLink(sLinkName, bRecursive) 读取符号链接 sLinkName 的目标路径。 如果 bRecursive 为 `true`，且目标仍为链接，则会递归解析直到得到非链接的有效路径。 返回符号链接指向的路径；当链接无效或目标不存在且 bRecursive 为 `true` 时返回空字符串。
SysUtils.ExtractFileName | sName = SysUtils.ExtractFileName(sFileName) 从完整路径中提取文件名部分。 文件名为最后一个目录分隔符（"/" 或 "\"）或盘符之后的所有字符。
SysUtils.ExtractFileExt | sExt = SysUtils.ExtractFileExt(sFileName) 返回文件的扩展名（最后一个点之后的所有字符，包含点字符）。
SysUtils.ExtractFilePath | sPath = SysUtils.ExtractFilePath(sFileName) 从文件名中提取路径部分（包含盘符）。 路径为最后一个目录分隔符（"/" 或 "\"）之前的所有字符，包含该分隔符本身。
SysUtils.ExtractFileDir | sDir = SysUtils.ExtractFileDir(sFileName) 仅提取 sFileName 的目录部分，包含盘符。 该目录名不包含结尾的目录分隔符，这点不同于 `SysUtils.ExtractFilePath`。
SysUtils.ExtractFileDrive | sDrive = SysUtils.ExtractFileDrive(sFileName) 从文件名中提取盘符部分。 注意，某些操作系统不支持盘符概念。
SysUtils.GetAbsolutePath | sName = SysUtils.GetAbsolutePath(sFileName, sBaseDirectory) 返回文件的绝对（完整）路径： sFileName : 带相对路径的文件名。 sBaseDirectory : 用作 sFileName 基准的目录。 如果无法获得绝对路径，函数将返回原始的 sFileName 值。
SysUtils.GetRelativePath | sName = SysUtils.GetRelativePath(sFileName, sBaseDirectory) 返回相对于指定目录的文件名： sFileName : 完整的（绝对）文件名。 sBaseDirectory : 用作 sFileName 基准的目录。 如果 sFileName 与 sBaseDirectory 相同，函数将返回空字符串（""）。若无法计算相对路径，则返回原始的 sFileName 值。
SysUtils.MatchesMask | bResult = SysUtils.MatchesMask(sFileName, sMask, iMaskOptions) 如果 sFileName 与给定掩码 sMask 匹配则返回 `true`。 iMaskOptions（可选，默认值 0）由以下值相加而成：
值 | 说明
--- | ---
1 | 区分大小写
2 | 忽略重音符号和连字
4 | Windows 风格过滤："*.*" 也会匹配无扩展名的文件等。
8 | 启用拼音支持（将使用文件 `pinyin.tbl`）
SysUtils.MatchesMaskList | bResult = SysUtils.MatchesMaskList(sFileName, sMaskList, sSeparator, iMaskOptions) 如果 sFileName 与以 sSeparator 分隔的掩码列表 sMaskList 中的任一项匹配，则返回 `true`（默认分隔符为 ";"）。 sSeparator and iMaskOptions (see above) are optional parameters.
SysUtils.Execute | bResult = SysUtils.Execute(sCommand) 执行 sCommand 命令，类似于在命令行中输入命令。成功时返回 `true` ，否则返回 `false` （该函数不会等待启动的命令执行完毕）。 注意：在 Windows 系统中，使用 os.execute 函数每次都会打开终端窗口，而 `SysUtils.Execute` 函数则无此问题。
SysUtils.ExpandEnv | sResult = SysUtils.ExpandEnv(String, bSpecial) 返回将其中所有环境变量替换为当前用户对应值的字符串。 bSpecial —— 此为可选参数：若为 `true` ，则同时会展开伪环境变量（详见 此处 说明）。默认值为 `false` 。
SysUtils.GetTempName | sTempFileName = SysUtils.GetTempName() 返回一个可用作临时文件的文件名（位于系统临时文件目录中），类似于 os.tmpname 函数，但文件将创建在 `_dc~~~` 子目录下，该子目录会在 Double Commander 关闭时自动删除。 如果函数无法创建唯一名称，则将返回空字符串。
SysUtils.GetFileProperty | Result = SysUtils.GetFileProperty(sFileName, iValue) 返回与 iValue 对应的文件属性：
值 | 说明
--- | ---
0 | 文件大小（字节）
1 | 文件面板中显示的属性字符串（关于此字符串值的详细说明请参阅 此处 ）
2 | 文件所属组
3 | 文件所有者
4 | 修改日期
5 | 创建日期
6 | 最后访问日期
7 | 状态更改日期
8 | 文件类型（如Windows资源管理器中的类型或MIME类型）
9 | 来自 `descript.ion` 文件的描述（注释）（详见 此处 ）
返回数值（文件大小、时间戳）或字符串（其他情况）。如果操作失败，函数返回 `nil` 。
SysUtils.FileSetTime | bResult = SysUtils.FileSetTime(sFileName, iModification, iCreation, iLastAccess) 允许为 sFileName 设置时间戳（Unix时间格式）：修改时间、创建时间和最后访问时间。对于需要忽略的时间戳，请指定为零。 成功时返回 `true` ，否则返回 `false` 。
SysUtils.PathDelim | SysUtils.PathDelim 当前操作系统用于分隔路径中目录名称的字符。 在 Unix/Linux 系统上为 "/"，在 Windows 上为 "\"。

（返回顶部）

## 3.2.1. SysUtils.FileGetAttr 返回值详解

`FileGetAttr` 返回文件 sFileName 的属性设置。

属性值是通过按位或（OR）组合以下常量得到的：

SysUtils.FileGetAttr 返回值中使用的常量
值 | 说明
--- | ---
0x00000001 faReadOnly | 文件为只读。
0x00000002 faHidden | 文件为隐藏文件。 在 Unix/Linux 中，这通常意味着文件名以点号（.）开头。
0x00000004 faSysFile | 系统文件。 在 Unix/Linux 中，这通常表示该文件是字符或块设备、命名管道 (FIFO) 等。
0x00000008 faVolumeId | 卷标。 仅适用于旧式 FAT 文件系统（非 VFAT 或 FAT32）的 DOS/Windows。
0x00000010 faDirectory | 表示该条目为目录。
0x00000020 faArchive | 归档位（标记为已归档）。 在 Unix/Linux 中无此概念。
0x00000400 faSymLink | 表示该条目为符号链接。
注意：发生错误时，函数返回 -1。

示例见 下一节。

（返回顶部）

## 3.2.2. SysUtils.FileGetAttr 示例

下面的脚本演示了如何使用 `SysUtils.FileGetAttr`。

如果检测到传入参数是目录，脚本将会在活动面板中打开一个新标签并切换到该目录。

    local params = {...}
    local iAttr

    if #params == 1 then -- 我们至少得到了一个参数？
      iAttr = SysUtils.FileGetAttr(params[1])
      if iAttr > 0 then -- 我们得到了一个有效的属性？
        if math.floor(iAttr / 0x00000010) % 2 ~= 0 then
          -- 第4位被置位？那么它是一个目录。
          DC.ExecuteCommand("cm_NewTab")
          DC.ExecuteCommand("cm_ChangeDir", params[1])
        end
      end
    end

在上例中，params[1] 是传递给脚本的第一个参数。

使用内部命令 cm_ExecuteScript 时，该参数会作为脚本文件名之后的第一个参数传入。

因此在本示例中，我们可以为工具栏配置类似的按钮：

在本示例中，参数 `%"0%p` 会被传递给脚本。该参数表示（不带引号）在我们按下工具栏按钮时活动面板中当前选中的项目的文件名。

（返回顶部）

## 3.2.3. 使用 FindFirst、FindNext 与 FindClose 的示例

下面的脚本示例会扫描传入参数指定的目录内容，并将结果写入第二个参数指定的文本文件中。

该示例展示了如何使用 `FindFirst`、`FindNext` 和 `FindClose`。

    local params = {...}

    if #params == 2 then -- 我们得到了两个参数？
      local Result = nil
      local hOutputFile = nil

      hOutputFile = io.output(params[2])

      local Handle, FindData = SysUtils.FindFirst(params[1] .. "\\*")
      if Handle ~= nil then
        repeat
          io.write(FindData.Name .. "\r")
          io.write(FindData.Size .. "\r")
          io.write("---------------\r")

          Result, FindData = SysUtils.FindNext(Handle)
        until Result == nil

        SysUtils.FindClose(Handle)
        io.close(hOutputFile)
      end
    end

在上例中，需要向脚本传入两个参数：

1. params[1] — 要扫描的目录。
2. params[2] — 用于保存结果的输出文件名。

因此可以通过内部命令 cm_ExecuteScript 配置一个工具栏按钮并传入这些参数来完成此操作。

在本示例中，参数 `%"0%Ds` 将作为第一个参数传递给脚本。该参数表示（不带引号）活动面板当前显示的目录。

（返回顶部）

## 3.3. 剪贴板库

Double Commander 为 Lua 脚本提供与系统剪贴板交互的功能。

下面的表格列出了相关函数：

剪贴板库
函数名 | 说明
--- | ---
Clipbrd.Clear | Clipbrd.Clear() 清空剪贴板内容。
Clipbrd.GetAsText | sVar = Clipbrd.GetAsText() 获取剪贴板当前的文本内容并返回到 sVar。如果剪贴板不包含文本，则返回空字符串。
Clipbrd.SetAsText | Clipbrd.SetAsText(sVar) 将字符串 sVar 的文本内容存入剪贴板。
Clipbrd.SetAsHtml | Clipbrd.SetAsHtml(sHtml) 将 HTML 格式的文本 sHtml 添加到剪贴板（采用 `CF_HTML` 剪贴板格式）。 支持该剪贴板格式的应用（如 MS Word、LibreOffice Writer 等）在粘贴时会使用该格式。 可以同时使用 `Clipbrd.SetAsText` 和 `Clipbrd.SetAsHtml` 保存数据，粘贴时应用会选择其支持的最佳格式。 例如： `Clipbrd.SetAsText("Welcome to Double Commander!")` `Clipbrd.SetAsHtml("Welcome to <b>Double Commander</b>!")` 如果在记事本中粘贴，将得到纯文本（由 `Clipbrd.SetAsText` 提供）；但在 Microsoft Word 中粘贴时，会使用 HTML 格式并显示加粗的 Double Commander。

（返回顶部）

## 3.3.1. 剪贴板库 使用示例

下面的示例使用了与剪贴板相关的三个函数：`Clear`, `GetAsText` 和 `SetAsText`。

脚本相对较长，但示例把我们前面见过的若干函数组合在一起，便于理解它们如何协同工作。

假设当前活动面板所在目录中包含大量源文本文件。

假设剪贴板中最初包含一个单词，并且脚本会接收当前活动文件夹作为单一参数。

脚本会扫描该目录下的文件，逐个读取文件内容以检测是否存在包含剪贴板中单词的文本行。

随后，所有包含至少一行匹配文本的文件名将被放入剪贴板。

然后脚本将使用内部命令 cm_LoadSelectionFromClip，这样包含匹配单词的文件就会被选中。

最后，脚本会把原本用于搜索的单词恢复回剪贴板中。

    local params = {...}
    local Result = nil
    local iAttr
    local bFound = false
    local sCompleteFilename = ""
    local hInputFile = nil
    local sLine = ""
    local iPosS
    local iPosE
    local sFileToSelect = ""
    local sSearchString = ""

    if #params == 1 then -- 我们得到了我们的参数？
      sSearchString = Clipbrd.GetAsText() -- 搜索表达式。
      Clipbrd.Clear() -- 确保我们的剪贴板无内容。
      DC.ExecuteCommand("cm_MarkUnmarkAll") -- 确保没有选中项。

      -- 逐个扫描文件夹内的所有文件。
      local Handle, FindData = SysUtils.FindFirst(params[1] .. "\\*")
      if Handle ~= nil then
        repeat
          sCompleteFilename = params[1] .. "\\" .. FindData.Name
          iAttr = SysUtils.FileGetAttr(sCompleteFilename)
          if iAttr > 0 then -- 我们得到了一个有效的属性？
            -- 我们需要文件，不是文件夹！
            if math.floor(iAttr / 0x00000010) % 2 == 0 then

              -- 逐行读取文件，直到文件末尾或找到匹配项为止。
              hInputFile = io.open(sCompleteFilename, "r")
              bFound = false

              while bFound == false do
                sLine = hInputFile:read()
                if sLine == nil then break end
                iPosS, iPosE = string.find(sLine, sSearchString)
                if iPosS ~= nil then bFound = true end
              end

              if bFound == true then
                sFileToSelect = sFileToSelect .. FindData.Name .. "\n"
              end

              io.close(hInputFile)
            end
          end
          Result, FindData = SysUtils.FindNext(Handle)
        until Result == nil

        SysUtils.FindClose(Handle)
      end

      -- 如果我们找到什么，选中它！
      if sFileToSelect ~= "" then
        Clipbrd.SetAsText(sFileToSelect)
        DC.ExecuteCommand("cm_LoadSelectionFromClip")
      end

      Clipbrd.SetAsText(sSearchString) -- 恢复剪贴板内容。
    end

（返回顶部）

## 3.4. 对话框库

该库允许脚本与用户交互，显示消息、提示输入等。

下面的表格列出相关函数：

对话框库
函数名 | 说明
--- | ---
Dialogs.MessageBox | iButton = Dialogs.MessageBox(sMessage, sTitle, iFlags) 显示一个消息框，用户按下的按钮会作为返回值返回： sMessage：消息框中的文本。 sTitle：消息框的标题文本。 iFlags：按位或的常量值，用于控制显示哪些按钮、窗口样式和默认按钮。参见下面关于 显示按钮、窗口样式 与 默认按钮 的说明。 iButton：返回值，表示用户按下的按钮（见 返回值表）。
Dialogs.InputQuery | bResult, sAnswer = Dialogs.InputQuery(sTitle, sMessage, bMask, sDefault) 显示一个请求输入的对话框，用户可以输入字符串值： sTitle：对话框标题。 sMessage：对话框中的提示信息。 bMask：布尔值，若为 true，则以“星号”隐藏输入字符（用于密码输入）。 sDefault：默认建议值，用户可在此基础上编辑。 bResult：返回的布尔值，指示用户是否实际输入并确认。 sAnswer：当用户输入并点击确定时返回的字符串，否则为 `nil` 或空。
Dialogs.InputListBox | sItem, iItem = Dialogs.InputListBox(sTitle, sMessage, aItems, sDefault) 显示一个供用户从列表中选择的对话框： sTitle：对话框标题。 sMessage：对话框中的说明文本。 aItems：一个 Lua 表，每个元素应为字符串，表示列表项。 sDefault：列表中默认选中的项。 sItem：返回所选项的字符串，若取消则返回 `nil`。 iItem：所选项的索引（从 1 开始，符合 Lua 表的惯例）。

（返回顶部）

## 3.4.1. Dialogs.MessageBox 中显示的按钮

`Dialogs.MessageBox` 中显示的按钮由按位或组合的标志控制，常见值如下：

Dialogs.MessageBox 显示按钮相关的常量
常量值 | 显示的按钮（从左到右）
--- | ---
0x0000 MB_OK |
0x0001 MB_OKCANCEL |
0x0002 MB_ABORTRETRYIGNORE |
0x0003 MB_YESNOCANCEL |
0x0004 MB_YESNO |
0x0005 MB_RETRYCANCEL |

（返回顶部）

## 3.4.2. Dialogs.MessageBox 的窗口样式

`Dialogs.MessageBox` 的图标与样式由按位或组合的常量控制，常见值如下：

Dialogs.MessageBox 图标与窗口样式相关的常量
常量值 | 窗口样式
--- | ---
0x0040 MB_ICONINFORMATION | 信息窗口
0x0030 MB_ICONWARNING | 警告窗口
0x0020 MB_ICONQUESTION | 确认窗口
0x0010 MB_ICONERROR | 错误窗口

（返回顶部）

## 3.4.3. Dialogs.MessageBox 的默认按钮

`Dialogs.MessageBox` 的默认活动按钮由按位或组合的常量控制，常见值如下：

Dialogs.MessageBox 默认按钮相关的常量
常量值 | 默认按钮
--- | ---
0x0000 MB_DEFBUTTON1 | 默认为最左侧的按钮
0x0100 MB_DEFBUTTON2 | 默认为从左数第二个按钮
0x0200 MB_DEFBUTTON3 | 默认为从左数第三个按钮

（返回顶部）

## 3.4.4. Dialogs.MessageBox 的返回值

`Dialogs.MessageBox` 返回的数值表示用户按下了哪个按钮，含义如下：

Dialogs.MessageBox 返回值与按下按钮的对应关系
常量值 | 按下的按钮
--- | ---
0x0000 mrNone | 无按钮被按下
0x0001 mrOK |
0x0002 mrCancel |
0x0003 mrAbort |
0x0004 mrRetry |
0x0005 mrIgnore |
0x0006 mrYes |
0x0007 mrNo |

注意：如果点击窗口右上角的“×”或按 `Esc` 关闭窗口，函数将返回“取消”按钮的值。

（返回顶部）

## 3.4.5. Dialogs.MessageBox 使用示例

下面是一个使用 `Dialogs.MessageBox` 的小脚本及其将显示的窗口：

    -- 显示的按钮
    MB_OK = 0x0000
    MB_OKCANCEL = 0x0001
    MB_ABORTRETRYIGNORE = 0x0002
    MB_YESNOCANCEL = 0x0003
    MB_YESNO = 0x0004
    MB_RETRYCANCEL = 0x0005

    -- 窗口样式
    MB_ICONINFORMATION = 0x0040
    MB_ICONWARNING = 0x0030
    MB_ICONQUESTION = 0x0020
    MB_ICONERROR = 0x0010

    -- 默认按钮
    MB_DEFBUTTON1 = 0x0000
    MB_DEFBUTTON2 = 0x0100
    MB_DEFBUTTON3 = 0x0200

    -- 按钮按下的返回值
    mrNone = 0x0000
    mrOK = 0x0001
    mrCancel = 0x0002
    mrAbort = 0x0003
    mrRetry = 0x0004
    mrIgnore = 0x0005
    mrYes = 0x0006
    mrNo = 0x0007

    iFlags = MB_YESNO + MB_ICONQUESTION + MB_DEFBUTTON2
    iButton = Dialogs.MessageBox("你想退出吗？", "询问", iFlags)

    if iButton == mrYes then
      DC.ExecuteCommand("cm_Exit")
    end

（返回顶部）

## 3.4.6. Dialogs.InputQuery 使用示例

下面是一个使用 `Dialogs.InputQuery` 的示例脚本及其显示的窗口：

    bResult, sAnswer = Dialogs.InputQuery("鉴权", "输入你的名字：", false, "John")

    if bResult == true then
      Dialogs.MessageBox("你好 " .. sAnswer .. "！", "欢迎光临！", 0x0040)
    end

（返回顶部）

## 3.5. UTF-8 库

该库为 UTF-8 编码提供基本支持。

所有函数都包含在表 `LazUtf8` 中。

UTF-8 库
函数名 | 说明
--- | ---
LazUtf8.Pos | iResult = LazUtf8.Pos(SearchText, SourceText, Offset) 在字符串中从指定位置查找子串，区分大小写。 返回从位置 Offset（默认 1）开始在 SourceText 中首次出现 SearchText 的位置。 若在指定位置之后未找到子串，则返回 0。
LazUtf8.Next | LazUtf8.Next(String) 一个迭代器函数，每次调用返回字符串 String 中的下一个字符及该字符在字节流中的起始位置。 示例：
    -- 以“位置 : 字符”的形式打印数值对
    for iPos, sChar in LazUtf8.Next(String) do
      DC.LogWrite(iPos .. " : " .. sChar)
    end
LazUtf8.Copy | sResult = LazUtf8.Copy(String, iIndex, iCount) 复制字符串的一部分。 返回从位置 iIndex 开始的 iCount 个字符组成的新字符串。 若 iCount 大于字符串长度则会截断；若 iIndex 超出字符串长度则返回空字符串。
LazUtf8.Length | iResult = LazUtf8.Length(String) 返回字符串中 UTF-8 字符的个数（字符数，而非字节数）。
LazUtf8.UpperCase | sResult = LazUtf8.UpperCase(String) 把字符串中的小写字母转换为大写并返回转换后的字符串副本。
LazUtf8.LowerCase | sResult = LazUtf8.LowerCase(String) 把字符串中的大写字母转换为小写并返回转换后的字符串副本。
LazUtf8.ConvertEncoding | sResult = LazUtf8.ConvertEncoding(String, FromEnc, ToEnc) 将 String 的编码从 FromEnc 转换为 ToEnc。 支持的编码列表： 默认系统编码（取决于系统区域设置）："default"。 默认 ANSI（Windows）编码（取决于系统区域设置）："ansi"。 默认 OEM（DOS）编码（取决于系统区域设置）："oem"。 Unicode："utf8"，"utf8bom"，"ucs2le"，"ucs2be"。 ANSI (Windows)："cp1250", "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258"。 OEM (DOS)："cp437", "cp850", "cp852", "cp865", "cp866", "cp874", "cp932", "cp936", "cp949", "cp950"。 ISO 8859："iso88591", "iso88592", "iso88593", "iso88594", "iso88595", "iso88597", "iso88599", "iso885910", "iso885913", "iso885914", "iso885915", "iso885916"。 其他: "macintosh", "koi8r", "koi8u", "koi8ru"。 特殊编码的含义（示例）： 在 Windows 系统中（以英语或俄语区域为例）： "默认" - cp1252 或 cp1251 "ansi" - cp1252 或 cp1251 "oem" - cp850 或 cp866 在 Linux 系统中（以英语或俄语区域为例）： "默认" - utf8 "ansi" - cp1252 或 cp1251 "oem" - cp850 或 cp866
LazUtf8.DetectEncoding | sEnc = LazUtf8.DetectEncoding(String) 检测并返回传入文本的编码类型。支持的编码与 `LazUtf8.ConvertEncoding` 中列出的相同。

（返回顶部）

## 3.6. 字符库

该库提供检查字符所属 Unicode 类别以及获取字符类别的函数。

本库中可用的函数列表：

字符库
函数名 | 说明
--- | ---
Char.GetUnicodeCategory | iResult = Char.GetUnicodeCategory(Character) 返回字符 `Character` 的 Unicode 类别，取值含义如下：
值 | 说明
--- | ---
Letter:
0 | 大写字母 (Lu)
1 | 小写字母 (Ll)
2 | 标题大小写字母 (Lt)
3 | 修饰字母 (Lm)
4 | 其他字母 (Lo)
Mark:
5 | 非间距标记 (Mn)
6 | 间距组合标记 (Mc)
7 | 包围标记 (Me)
Number:
8 | 十进制数字 (Nd)
9 | 字母数字 (Nl)
10 | 其他数字 (No)
Punctuation:
11 | 连接标点 (Pc)
12 | 破折号标点 (Pd)
13 | 开括号类标点 (Ps)
14 | 闭括号类标点 (Pe)
15 | 起始标点 (Pi)
16 | 结束标点 (Pf)
17 | 其他标点 (Po)
Symbol:
18 | 数学符号 (Sm)
19 | 货币符号 (Sc)
20 | 修饰符号 (Sk)
21 | 其他符号 (So)
Separator:
22 | 空格分隔符 (Zs)
23 | 行分隔符 (Zl)
24 | 段落分隔符 (Zp)
Other:
25 | 控制字符 (Cc)
26 | 格式字符 (Cf)
27 | 代理项 (Cs)
28 | 专用用途 (Co)
29 | 未分配 (Cn)
Char.IsDigit | bResult = Char.IsDigit(Character) 如果 Character 属于十进制数字类别（Nd），则返回 `true`。
Char.IsLetter | bResult = Char.IsLetter(Character) 如果 Character 属于字母类（Lu、Ll、Lt、Lm 或 Lo），则返回 `true`。
Char.IsLetterOrDigit | bResult = Char.IsLetterOrDigit(Character) 如果 Character 属于字母或数字类别（Lu、Ll、Lt、Lm、Lo、Nd 或 Nl），则返回 `true`。
Char.IsLower | bResult = Char.IsLower(Character) 如果 Character 属于小写字母类别（Ll），则返回 `true`。
Char.IsUpper | bResult = Char.IsUpper(Character) 如果 Character 属于大写字母类别（Lu），则返回 `true`。

此外，这些函数也支持两个参数的形式：与其传入单个字符，也可以提供一个字符串以及该字符在字符串中的位置来进行检测。

（返回顶部）

## 3.7. 操作系统库

该库包含与运行 Double Commander 的操作系统相关的函数。

下面是本库中可用函数的列表：

操作系统库
函数名 | 说明
--- | ---
os.execute | iResultCode = os.execute(sCommand) 在命令行中执行 sCommand，并返回操作的返回码。 sCommand 可以是： 终端命令，例如 `os.execute("dir > all.txt")` 可执行文件，例如 `os.execute("C:\\Windows\\System32\\calc.exe")` 带参数的可执行文件，例如： `os.execute("C:\\Utils\\fsum.exe -md5 test.bin > md5.txt")`
os.tmpname | sTempFileName = os.tmpname() 返回一个可用于临时文件的文件名（位于系统临时目录）。若无法创建唯一名称，则返回空字符串。
os.remove | bResult, sError, iError = os.remove(sFileName) 删除名为 sFileName 的文件或目录。 成功时函数返回 `true`；失败时函数返回三项： `nil` 表示操作失败 sError：错误消息描述 iError：错误代码数字
os.rename | bResult, sError, iError = os.rename(sOldName, sNewName) 将文件 sOldName 重命名为 sNewName。 注意： 若目标文件 sNewName 已存在，将被覆盖！ 成功时返回 `true`；失败时返回三项（同 `os.remove`）：`nil`, sError, iError。
os.getenv | Value = os.getenv(VariableName) 返回环境变量 VariableName 的值；若该变量不存在，则返回 `nil`。
os.setenv | os.setenv(VariableName, Value) 添加或修改环境变量 VariableName 的值。发生错误时函数返回 -1。
os.unsetenv | os.unsetenv(VariableName) 移除环境变量 VariableName。发生错误时函数返回 -1。

（返回顶部）

## 4. 索引

DC 库 DC.CurrentPanel DC.ExecuteCommand DC.ExpandVar DC.GetPluginField DC.GoToFile DC.LogWrite 系统库 SysUtils.CreateDirectory SysUtils.CreateHardLink SysUtils.CreateSymbolicLink SysUtils.DirectoryExists SysUtils.Execute SysUtils.ExpandEnv SysUtils.ExtractFileDir SysUtils.ExtractFileDrive SysUtils.ExtractFileExt SysUtils.ExtractFileName SysUtils.ExtractFilePath SysUtils.FileExists SysUtils.FileGetAttr SysUtils.FileSetTime SysUtils.FindClose SysUtils.FindFirst SysUtils.FindNext SysUtils.GetAbsolutePath SysUtils.GetFileProperty SysUtils.GetRelativePath SysUtils.GetTempName SysUtils.GetTickCount SysUtils.MatchesMask SysUtils.MatchesMaskList SysUtils.PathDelim SysUtils.ReadSymbolicLink SysUtils.RemoveDirectory SysUtils.Sleep | 剪贴板库 Clipbrd.Clear Clipbrd.GetAsText Clipbrd.SetAsHtml Clipbrd.SetAsText 对话框库 Dialogs.InputListBox Dialogs.InputQuery Dialogs.MessageBox UTF-8 库 LazUtf8.ConvertEncoding LazUtf8.Copy LazUtf8.DetectEncoding LazUtf8.Length LazUtf8.LowerCase LazUtf8.Next LazUtf8.Pos LazUtf8.UpperCase 字符库 Char.GetUnicodeCategory Char.IsDigit Char.IsLetter Char.IsLetterOrDigit Char.IsLower Char.IsUpper | 操作系统库 os.execute os.getenv os.remove os.rename os.setenv os.tmpname os.unsetenv

（返回顶部）