问题起源
在使用 AHK 写稍大些的项目时,就会拆分文件,将脚本存放到不同的位置;同时,也会引用别人的工具类等。
这些情况下就会使用#Include
导入脚本;可能是绝对路径,也可能是相对路径。
在绝对路径的情况下,想要将项目分享给他人就会带来许多麻烦。因为需要解决绝对路径导入问题。
我曾经的做法是写了一个AHK脚本Merge.ahk
,将依赖的脚本全部整合到一个脚本中,然后引入即可;但这样做的缺点太明显了,所以没多久就废弃了。
这段时间,我写好了截图工具这个项目将它上传到github上了,就出现了引入脚本的问题;这迫使我写出更好的脚本来处理这种情况。
处理
我的做法是:
- 将以绝对路径导入的脚本都复制一份,放入到
Lib
文件夹中; - 对于这些脚本,内部使用的
#Include
脚本同样添加进去。
这样,即解决了路径问题,又保留了可读性,不会对项目造成任何干扰。
运行截图
下面是脚本的运行截图,以脚本本身为例(自己也使用了外部脚本):
其中的包含关系是:
- IncludeTransfer.ahk 包含 path.ahk 和 extend.ahk
- path.ahk 包含 extend.ahk
- extend.ahk 包含 extend/xx.ahk (许多个)
将外部脚本映射到lib目录中。
形成的目录结构。
此时导入路径也修改成正确的路径。
代码
下面的代码可以参考(无法直接运行),也可以访问仓库,我为其独立建立了一个仓库,同时使用此脚本处理了自己,所以可以直接运行。
#Requires AutoHotkey v2.0
; 将以绝对路径导入的文件复制到 ./lib 中
; 对于绝对路径导入的文件,需要将其依赖也导入
#SingleInstance Force
#Include G:\AHK\git-ahk-lib\Path.ahk
libName := '_lib', eof := '`r`n'
g := ResolveGui()
g.Show()
; =============
libPath := '' ; lib 的绝对路径,选择时确定
included := Map() ; 避免重复导入,同时辅助导入文件
statisic := {
external: 0,
}
filters := [RemoveComment]
includeRE := 'i)^#include\s+([^;\s]+)' ; 匹配导入语句,只支持导入脚本,而其他导入类型**不适用**
; 参数:
; 文件实际路径 | 写入路径 | 父脚本是否为外部脚本
Resolve(_path, _targetPath, _external := false) {
if !FileExist(_path)
throw Error('bad filepath: ' _path ' type: ' _external)
if included.Has(StrLower(_path)) ; 避免重复导入
return
included.Set(StrLower(_path), _targetPath) ; 实际的 : 转换的
if _external {
statisic.external++
g.Log('[E]Mapping: ' _path ' => ' _targetPath)
} else {
g.Log('[I]Mapping: ' _path ' => ' _targetPath)
}
; 开始处理
f := FileOpen(_path, 'r', 'utf-8'), r := ''
while !f.AtEOF {
if not l := f.ReadLine() { ; 空行
r .= eof
continue
}
/* 在此运行过滤器 */
; if _external {
; _Trim_(&l)
; }
curDir := Path.Dir(_path)
if RegExMatch(l, includeRE, &match) {
ip := Trim(match[1], '[`'|"]') ; 去除可能的引号
; 将绝对路径约定为外部脚本;
; 如果以绝对路径导入项目中的脚本,将导致该脚本被添加到 lib 目录中(并不会影响运行)
if Path.IsAbsolute(ip) {
np := Path.Join(libPath, n := Path.Parse(ip).name) ; 转换为新路径,表示要将内容写到此处
if _external {
r .= '#Include ' Path.Relative(Path.Dir(_targetPath), np) eof ; 新的引用路径
} else {
r .= '#Include ' Path.Relative(curDir, np) eof
}
Resolve(ip, np, true) ; 递归处理
continue
} else { ; 相对路径
np := Path.Join(curDir, ip) ; 计算绝对路径
; 如果以 .. 开头,在外部脚本中需要特殊处理;
; 而项目脚本不许要,正常处理即可。
if SubStr(ip, 1, 2) = '..' {
rp := Path.Join(curDir, ip) ; 计算实际路径
if _external { ; 如果是外部脚本的相对路径
if included.Has(_ := StrLower(rp)) { ; 已经包含
r .= '#Include ' Path.Relative(Path.Dir(_targetPath), _ := included.Get(_)) eof
Resolve(rp, _, true)
} else { ; 当作绝对路径处理
np := Path.Join(libPath, n := Path.Parse(rp).name)
r .= '#Include ' Path.Relative(Path.Dir(_targetPath), np) eof
Resolve(rp, np, true)
}
continue
}
}
; ./
r .= '#Include ' Path.Normalize(ip) eof
targetDir := Path.Parse(_targetPath).dir
CreateDirIfNotExist targetDir
Resolve(np, Path.Join(targetDir, ip), _external)
continue
}
}
r .= l eof
}
f.close()
f := FileOpen(_targetPath, 'w', 'utf-8') ; 输出
f.Write(r)
f.Close()
return
_Trim_(_l) {
for fn in filters
fn(&_l)
return _l ? _l '`r`n' : ''
}
}
CreateDirIfNotExist(dirPath) {
if !DirExist(dirPath) {
DirCreate(dirPath)
}
}
RemoveComment(&line) {
copy := line
if copy ~= '^\s*;.*$'
copy := ''
else if RegExMatch(copy, '(.*?)\s+;.*?$', &match)
copy := match[1]
line := copy
}
class ResolveGui extends Gui {
f := ''
__New() {
super.__New()
this.SetFont('s14', 'Consolas')
this.AddText('', 'A simple tool for resolve absolute included ahk scripts.')
this.AddEdit('ve w1400 h600 ReadOnly')
this.AddButton('yp section w80', 'Select').OnEvent('click', (*) => this.SelectFile())
this.AddButton('w80', 'Resolve').OnEvent('Click', (*) => this.Resolve())
this.OnEvent('DropFiles', (g, ctrl, files, *) => Resolve(files[1], libPath))
}
SelectFile(*) {
f := FileSelect(3, , 'Select a script file', 'Ahk root file(*.ahk)')
if f {
if this.f
this.Log('Entry file has been changed: ' f)
else
this.Log('Entry file has been selected: ' f)
this.Log('Make sure to BACKUP your files before resolving.')
this.f := f
}
}
Resolve() {
if !this.f {
this.Log('Select a file first.')
return
}
global libPath := Path.Join(Path.Dir(this.f), libName)
CreateDirIfNotExist libPath
this.Log('==========START==========')
this.Log('Resolving file: ' this.f)
Resolve(this.f, this.f)
this.Log('===========END===========')
this.Log('========STATISTIC========')
this.Log('External scripts: ' statisic.external)
this.Log('Internal scripts: ' included.Count - statisic.external)
this.Log('===========END===========')
this.f := ''
}
Log(t) => this['e'].Value .= '>> ' t '`n'
}
标签:脚本,Log,AHK2,ahk,绝对路径,导入,相对路径,Path
From: https://www.cnblogs.com/refiz/p/18410157