# 脚本

## 目录

- 介绍
- 脚本顶部(自动执行部分): 当脚本启动时，这部分会自动地执行。
- 转义顺序: 何时要使用 `% 和 `, 来表示原义的百分号或逗号。
- 脚本中的注解: 使用分号和 /*...*/ 符号给脚本添加备注。
- 把一个过长的行分割成一系列短行: 这样可以改善脚本的可读性和可维护性。
- 把脚本转换成 EXE 文件(ahk2exe): 把一个 .ahk 脚本转换成一个能在任何 PC 上运行的 .exe 文件。
- 向脚本传递命令行参数: %1%, %2% 等等包含了输入参数的变量。
- 调试脚本: 如何在不能正常工作的脚本中找到的问题。
- AutoHotkey.exe 的可移植性: 拥有一个 AutoHotkey.exe 的备份即可执行任何 .ahk 文件。
- 安装程序的选项: 如何进行无人值守/静默的安装或卸载。

## 介绍

每个脚本都是一个纯文本文件，其中包含了要被程序(AutoHotkey.exe)执行的行。一个脚本也可以包含热键和热字符串，或者完全由它们构成。不过，在没有热键和热字符串的情况下，脚本会在启动的那刻起，从上到下顺序地执行其中的命令。

程序会将脚本逐行的读取到内存中，每行最多可以包含 16,383 个字符。在读取过程中，脚本将被优化并确认。任何语法错误都将被列出，它们必须被纠正后脚本才能运行。

## 脚本顶部(自动执行部分)

在脚本被读取完毕后，它会从第一行开始执行，直至遇到 Return, Exit, 热键/热字符串标签或者脚本的底部(无论最先遇到哪个)。脚本的这个顶部被称为自动执行部分。

一个非持续的并且没有热键、热字符串、OnMessage 和 GUI 的脚本，将会在自动执行部分结束后终止。否则，它会以空闲状态继续运行，从而对例如热键、热字符串、GUI 事件、自定义菜单项和定时器这些事件作出反应。

如果在自动执行部分设置了下列属性，那么由热键、热字符串、菜单项、GUI 事件或定时器启动的每个线程都将以它们的默认值重新开始执行。如果未设置，则应用标准的默认值(如下列每个页面中说明的那样)：DetectHiddenWindows, DetectHiddenText, SetTitleMatchMode, SetBatchLines, SendMode, SetKeyDelay, SetMouseDelay, SetWinDelay, SetControlDelay, SetDefaultMouseSpeed, CoordMode, SetStoreCapslockMode, AutoTrim, SetFormat, StringCaseSense, Thread 以及 Critical。

如果自动执行部分需要较长时间才能完成(或者无法完成)，那么上面这些设置的默认值将会在 100 毫秒之后生效。当自动执行部分最终完成(如果能的话)，那些在自动执行部分结尾处已生效的默认值才被再次更新。因此，通常最好在包含热键、热字符串、定时器或者自定义菜单项的脚本的顶部对默认值做出想要的更改。还要注意，每个线程会保持它自有的上述设置的集合。对那些设置所做的更改不会影响到其他线程。

## 转义顺序

AutoHotkey 的默认转义字符是重音符/反引号(`)，它位于大多数英文键盘的左上角。使用这个字符而不是反斜线，可以避免在文件路径中对双反斜线的需要问题。

由于在 AutoHotkey 语言中逗号和百分号有着特殊的意义，因此可以用 `, 来指定一个原义的逗号，用 `% 来指定一个原义的百分号。MsgBox 是一个例外，它不需要将逗号转义。另一个例外是，任何命令的最后一个参数中的逗号：它们不需要被转义。有关转义顺序的完整列表，请看 #EscapeChar。

某些特殊字符也需要通过转义顺序来生成。最常见的有 `t (tab), `n (换行) 和 `r (回车)。

提示：任何命令的第一个逗号都可以省略(当第一个参数为空或者命令单独处于连续部分的首行这两种情况除外)。例如：

    MsgBox 这样可以。
    MsgBox, 这样也可以 (它有一个明显的逗号)。

## 脚本中的注解

在行首使用一个分号可以注解脚本。例如：

    ; 这整行是一个注解。

注解也可以添加到一个命令的末尾，这种情况下分号左侧必须至少有一个空格或 tab。例如：

    Run Notepad  ;这是一个和命令在同一行的注解。

另外，/* 和 */ 符号可以被用来注解掉整个部分，但是只有在符号出现在行首时才行，如下所示：

    /*
    MsgBox, 这一行被注解掉了(禁用)。
    MsgBox, 这行也是。
    */

由于在脚本启动时将忽略注解，所以他们不会影响性能或者内存利用率。

通过 #CommenFlag 可以把默认的注解符号(分号)更改为其它字符或字符串。

## 把一个过长的行分割成一系列短行

过长的行可以被分割成一系列较短的行来改善可读性和可维护性。这样做不会降低脚本的执行速度，因为在脚本启动的时候这些短行会在内存中被合并。

方法 #1：以 "and"、"or"、||、&&、逗号或句号开头的行会自动合并到它的上一行(在 1.0.46 及之后版本，除了 ++ 和 -- 以外的所有其他表达式运算符都会如此)。下例中，由于第二行以一个逗号开头，所以它被追加到第一行：

    FileAppend, 这是要追加的文本`n   ;这里允许使用注解。
        , %A_ProgramFiles%\SomeApplication\LogFile.txt  ;注解。

类似地，以下几行将被合并为一行，因为最后两行是以 "and" 和 "or" 开头的：

    if (Color = "Red" or Color = "Green"  or Color = "Blue"   ;注解。
        or Color = "Black" or Color = "Gray" or Color = "White")   ;注解。
        and ProductIsAvailableInColor(Product, Color)   ;注解。

三元运算符也是一个很好的候选：

    ProductIsAvailable := (Color = "Red")
        ? false  ;我们没有任何红色的产品，因此不用麻烦调用函数了。
        : ProductIsAvailableInColor(Product, Color)

尽管用在上面例子中的缩进不是必须的，但它可以指示哪行从属于上一行，这样可以改善代码的清晰度。此外，没有必要将额外的空格包括在以单词 "AND" 和 "OR" 开头的行里；程序会自动处理这些。最后，可以在上面例子中的任何行与行之间或者行尾添加空行或注解。

方法 #2：这个方法可以用来合并大量的行或者是方法 #1 不适用的行。尽管本方法对自动替换热字符串特别有用，但它同样可以用于任何命令或表达式。例如：

    ;例子 #1:
    Var =
    (
    第一行文本。
    第二行文本。默认情况下，将在两行中插入一个换行符(`n)。
    )

    ;例子 #2:
    FileAppend,  ;这种情况下需要逗号。
    (
    一行文本。
    默认情况下，上一行和本行之间的硬回车(Enter)将作为换行符(`n)写到文件中。
        默认情况下，本行左侧的 tab 也将写到文件中(空格也会一样)。
    默认情况下，例如 %Var% 这样的变量引用将被解析为变量的内容。
    ), C:\My File.txt

在上面的例子中，数行内容在头尾被一对圆括号围起来。这被称作连续部分。注意，最下面一行的右圆括号后包含了 FileAppend 的最后一个参数。这个习惯是可选的；此时这样做是以便将逗号视为一个参数分隔符而非一个原义的逗号。

通过在左括号的右侧包含一个或多个下列选项，可以覆盖连续部分的默认特性。如果存在多个选项，用它们彼此用一个空格分隔开。比如：( LTrim Join| %

Join: 指定行与行应该如何连接。如果省略此选项，除最后一行外，每一行后面都会跟一个换行符(`n)。如果单独指定单词 Join，行与行之间不添加任何字符而直接连接起来。否则，单词 Join 后面应该最多紧跟 15 个字符。例如，Join`s 将会在除最后一行外的每行末尾添加一个空格(`s 代表一个原义的空格，这是一个只能被 Join 识别的特殊的转义顺序)。再比如 Join`r`n，将在行与行之间插入 CR+LF (回车+换行)。类似地，Join| 将在行与行之间插入一个竖线。要让连续部分的最后一行也以一个 join 字符串结尾，只要在连续部分右括号的上一行包含一个空行即可。

LTrim: 省略每行头部的空格和 tab。主要被用来允许连续部分使用缩进。另外，通过在一行上单独指定 #LTrim 可以对多个连续部分开启此选项。#LTrim 是位置相关的：它会影响它下面所有的连续部分。可以通过 #LTrim Off 来关闭此选项。

RTrim0 (RTrim 后跟一个零): 将忽略每行末尾的空格和 tab 的设置关闭。

Comments (或 Comment 或 Com 或 C) [1.0.45.03 及之后版本]: 在连续部分内允许分号注解(但 /*..*/ 无效)。这种注解(连同它们左侧的任何空格和 tab)将从合并结果内完全忽略，而不会被当作原义的文本处理。每个注解都可以出现在一行的右侧或者单独另起一行。

% (百分号)：把百分号作为原义处理而非变量引用。这样就无需为每个百分号转义而使其成为原义。在百分号已经是原义的地方就不需要此选项了，比如自动替换热字符串。

, (逗号)：将逗号作为分隔符处理，而非其原义。这个罕用的选项只有在命令的参数之间才用得到，因为在函数调用中，逗号的类型不重要。并且，此选项只转换那些真正分隔参数的逗号。换句话说，一旦到达命令的最后一个参数(或者根本就没有参数)，随后的逗号就将作为原义逗号来对待从而忽略此选项。

` (重音符): 把每个反引号都作为原义来对待，而不是转义字符。这也防止了逗号和百分号被明确单独地转义。此外，它会防止任何明显被指定的转义顺序比如：`r 和 `t 的转换。

备注

除了重音符(`)选项被指定的时候外，在连续部分中是支持转义顺序的比如 `n(换行)和 `t (tab)。

当未指定 comment 选项时，在连续部分内部是不支持分号和 /*..*/ 注解的，因为它们被视为原义的文本。不过，在连续部分的顶部和底部行中可以包含注解，例如：

    FileAppend,   ;注解。
    ;注解。
    ( LTrim Join    ;注解。
         ; 这不是一个注解；它是原义的。在上一行添加单词 Comments 就可以使这行成为注解。
    ), C:\File.txt   ;注解。

由于上面的原因，在连续部分中分号不再需要被转义。

连续部分无法创建总长度超过 16,383 个字符的行(如果尝试创建，程序会在脚本启动时警告你)。解决方案是把一系列内容串联到一个变量中。例如：

    Var =
    (
    ...
    )
    Var = %Var%`n  ;通过另一个连续部分向变量添加更多文本。
    (
    ...
    )
    FileAppend, %Var%, C:\My File.txt

因为一个右括号标志着一个连续部分的结束，要让某一行以原义的右括号开头，可以在它前面用重音符/反引号开头：`)。

一个连续部分后面可以紧跟某个包含了另一个连续部分左括号的行。这就使得上面提及的那些选项在创建单行内容的过程中可以多样化。

不支持通过 #Include 的方式将一个个连续部分组合起来。

## 把脚本转换成 EXE 文件 (ahk2exe)

AutoHotkey 的程序中包括一个脚本编译器(受 Jonathan Bennett 的 AutoIt v3 源代码的恩惠)。不支持 AutoIt v2 的脚本，所以如果有必要的话，可以先将你的 .aut 文件自动转换成 .ahk 文件。

一旦脚本被编译，它就生成一个独立的可执行文件；就是说即便在没有安装 AutoHotkey 的机器上也可以运行(这样的 EXE 文件可以随意发布或出售)。编译过程中，下列所有文件将被压缩并加密：脚本、脚本 include 的任何文件，以及任何通过 FileInstall 命令合并的文件。

编译并不会改善脚本的性能。事实上已编译的脚本启动时会略慢，因为它必须先解密和解压缩到内存，这之后它就会像一个普通脚本那样被优化。

可以用下列方法使用 Ahk2Exe：

1. 图形界面: 在开始菜单中运行 "Convert .ahk to .exe"。在 1.0.46.10 及之后版本，可以在图形界面的密码框指定 N/A 来避免被 exe2ahk 反编译成脚本。此方法仅适用于图形界面；对于命令行方式，可用 /NoDecompile 开关来实现。
2. 右键点击: 在打开的资源管理器窗口内，你可以右键点击任何 .ahk 文件，选择 "Compile Script"(只有安装 AutoHotkey 时选择了 script compiler 选项才会有效)。稍后在同个目录下会产生一个和脚本同名的 EXE 文件。注意：会用和方法 1 中最后一次使用的自定义图标和压缩等级相同的设置来生成 EXE 文件，并且没有反编译密码。
3. 命令行: 编译器可以使用下列参数以命令行方式运行：

       Ahk2exe.exe /in MyScript.ahk [/out MyScript.exe][/icon MyIcon.ico][/pass password][/NoDecompile]

   例如：

       Ahk2exe.exe /in "MyScript.ahk" /icon "MyIcon.ico" /pass "CustomPassword" /NoDecompile

   用法：
   - 包含空格的参数应该置于双引号之间。
   - 如果省略 "out" 的文件名，EXE 文件会和脚本同名。
   - 在 1.0.46.10 及之后版本，即便给出了正确的密码，/NoDecopile 开关(如果有的话)也会阻止 exe2ahk 反编译脚本。这样就能在一个长而复杂的密码之外再提供一层额外的保护。

注意：

- 如果你打算发布自己的 EXE 文件，并且不想让任何人查看你的脚本源代码，你可以使用带 /NoDecompile 开关的命令行方式进行编译并指定一个长而复杂的密码，从而获得最大限度的保护。密码的最大长度为 64 个字符。
- #NoTrayIcon 和 "Menu, Tray, ShowMainWindow" 命令将会影响到编译后脚本的行为。
- 下载 Exe2Ahk (此工具应该在命令提示符中运行)就可以反编译 EXE 文件，从而提取原脚本。不过，原来脚本中存在的注解(分号或 /**/)都将丢失。
- 将你的 AutoHotkey\Compiler 文件夹中的 AutoHotkeySC.bin 文件替换为这个更小的版本(覆盖已存在的同名文件)，就可以使编译的脚本减少大概 20 KB 的大小。以这种方式产生的任何已编译脚本都将依赖 MSVCRT.dll。尽管这个 DLL 在 Windows 2000/XP 或之后的系统中总是存在，但较早的操作系统却不一定有。
- 使用 Resource Hacker(免费软件)这样的工具来编辑 "AutoHotkeySC.bin" 文件，就可以将自定义的版本信息(在资源管理器的文件属性对话框中看到的那些)添加到你编译的脚本中。该文件被包含在 AutoHotkey 安装目录的 "Compiler" 子文件夹中。注意：Resource Hacker 会破坏已编译的脚本，这就是为什么应该只编辑 AutoHotkeySC.bin 文件的原因。
- 上面的方法也可以用来改变所有已编译脚本中已存在的图标或者添加新的图标。
- 如果脚本是用已编译的形式运行，那么内置变量 A_IsCompiled 包含 1，否则为空。
- 如果你不希望你编译的脚本被压缩，只要删除或者重命名 AutoHotkey 的 "Compiler" 文件夹中的 "upx.exe" 文件即可。
- 当传递参数给 Ahk2Exe 时，一条关于编译过程成功与否的消息将会写入到标准输出。尽管消息不会显示在命令提示符中，但可以使用诸如重定向输出到一个文件的方式来“获取”。[1.0.43 及之后版本]

## 向脚本传递命令行参数

脚本支持命令行参数。格式为：

    AutoHotkey.exe [Switches] [Script Filename] [Script Parameters]

对于编译过的脚本，格式为：

    CompiledScript.exe [Switches] [Script Parameters]

Switches: 零个或多个下列开关：
/f 或 /force -- 无条件运行，跳过任何警告对话框。
/r 或 /restart -- 表明脚本将被重新加载(在内部，Reload 命令也使用这个开关)。
/ErrorStdOut -- 把妨碍脚本启动的语法错误发送到标准输出而不是显示一个对话框。详见 #ErrorStdOut。

Script Filename: 如果没有 Script Parameters 的话可以省略这个参数。如果省略了，将会运行(或者提示你创建)我的文档文件夹中的 AutoHotkey.ahk 文件。唯一的例外是，在当前的工作目录存在 AutoHotkey.ini 文件，这样的话就会运行那个文件。

Script Parameters: 你想要传递给脚本的字符串，彼此间用一个空格分隔。任何包含有空格的参数应该置于一对引号之间。要使用原义的引号，可以通过在它前面加一个反斜线(\")来传递。因此，任何引号内的参数末尾是反斜线的(例如："C:\My Documents\")将被作为一个原义的引号处理(就是说，脚本将会收到 C:\My Documents" 这样的字符串)。要移除这样的引号，请用 StringReplace, 1, 1, ",, All

脚本把传入的参数视为 %1%, %2% 等等这样的变量。另外，%0% 包含了传入参数的数量(如果没有则为 0)。在下面的例子中，如果传入的参数数量不够，脚本将会退出：

    if 0 < 3  ;非表达式 if 语句的左侧总是变量名。
    {
        MsgBox 此脚本需要至少三个传入参数，但只接收到 %0% 个。
        ExitApp
    }

如果传送给脚本的参数数量不定(可能是由于用户拖放了一组文件到脚本上)，下面的例子可以用来将它们逐个提取：

    Loop, %0%  ;对每个参数执行一次：
    {
        param := %A_Index%  ;提取 A_Index 包含的变量名称里的内容。
        MsgBox, 4,, 第 %A_Index% 个参数是 %param%。继续？
        IfMsgBox, No
            break
    }

如果参数是文件名，下面的例子可以用来把他们转换为大小写正确的长文件名(像在文件系统中存储的那样)，包括完全/绝对路径：

    Loop %0%  ;对每个参数(或者拖放到脚本上的文件)执行一次：
    {
        GivenPath := %A_Index%  ;提取 A_Index 包含的变量名称里的内容。
        Loop %GivenPath%, 1
            LongPath = %A_LoopFileLongPath%
        MsgBox 文件`n%GivenPath%`n的大小写正确的长路径名是：`n%LongPath%
    }

已知限制：如果 NTFS 文件系统已经关闭了的 8.3 (短)文件名，那么拖放到 .ahk 脚本上的文件将无法正常工作。一个解决方案是编译脚本，然后向编译产生的 EXE 拖放文件。

## 调试脚本

ListVars 和 Pause 等命令可以帮助你调试一个脚本。例如，下面这两行，当临时插入到精心选择的位置时，脚本会创建一个“断点”：

ListVars
Pause

当脚本运行到这两行的时候，将会显示你需要检视的所有变量的当前内容。当你准备恢复的时候，只要通过 File 或者托盘菜单反 pause 脚本即可。然后脚本将继续运行直到遇到下一个“断点”(如果有的话)。

通常最好把这些“断点”插入到激活的窗口和脚本没什么关系的位置，比如 WinActivate 命令的上一行。这样就允许你反 pause 脚本时，它能妥当地恢复操作。

以下几个命令对调试脚本也很有用：ListLines, KeyHistory 和 OutputDebug。

## AutoHotkey.exe 的可移植性

运行任何 .ahk 脚本就只需要 AutoHotkey.exe 这一个文件。唯一的例外是 Windows NT4，任何在这个系统上使用 Process 命令的脚本都需要复制一份 psapi.dll(从 AutoHotkey 文件夹)。

## 安装程序的选项

要静默安装 AutoHotkey 到默认目录(这也是非静默模式所显示的同一个目录)，只要把参数 /S 传递给安装程序即可( /S 必须是大写的)。例如：

    AutoHotkey104307_Install.exe /S

可以通过参数 /D 来指定默认路径以外的路径(没有参数 /S 的情况下，这将改变通过安装程序显示的默认路径)。例如：

    AutoHotkey104307_Install.exe /S /D=C:\Program Files\AutoHotkey

要静默地卸载 AutoHotkey，只要把参数 /S 传递给反安装程序即可。例如：

    "C:\Program Files\AutoHotkey\uninst.exe" /S

## 脚本展示

这个页面列出了一些有用的脚本。

翻译：wanggang999 修正：天堂之门 menk33@163.com 2008年11月27日