[ Skill ] load 函数优化,识别相对路径
https://www.cnblogs.com/yeungchie/
本篇博客所示代码仅作为基本思路介绍,后续代码优化等改动会在 GitHub 和 Gitee 进行,这里不再更新。
GitHub: https://github.com/yeungchie/skill-loader
Gitee: https://gitee.com/yeungchie/skill-loader
在 cds.lib 文件中定义库的路径,为了规范管理库的定义,经常这样做:
$ tree
.
|-- cds.lib ------------------- cat --> 1| INCLUDE ./common/cds.lib
`-- common
|-- cds.lib --------------- cat --> 1| INCLUDE ./project/cds.lib
| 2| INCLUDE ./project/cds.lib
|-- project
| |-- cds.lib ----------- cat --> 1| INCLUDE ./layout/cds.lib
| | 2| INCLUDE ./sch/cds.lib
| | 3| 略 ...
| |-- layout
| | `-- cds.lib ------- cat --> 1| DEFINE layout_lib1 ./layout_lib1
| | 2| DEFINE layout_lib2 ./layout_lib2
| | 3| 略略 ...
| |-- sch
| | `-- cds.lib ------- cat --> 1| 略略略 ...
| `-- 略略略略 ...
`-- 略略略略略 ...
可以看到,对于 cds.lib 文件来说,INCLDUE
/ DEFINE
都可以使用相对路径。
而在 Skill 中使用 load()
/ loadi()
函数却不能支持相对路径:
在上一篇随笔 文件读写 & IO 句柄 整理的内容中,发现内置的句柄 piport
可以获取当前文件的路径这一特性,于是有了这一篇。
几个函数
ycGetFileName
用来获取当前文件的路径,功能类似 Perl 中的 __FILE__
函数。
procedure(ycGetFileName()
get_filename(piport)
)
-
创建一个
/home/yeung/tmp/load.il
脚本并运行:-
脚本内容
printf("Current path: %s\n" ycGetFileName())
-
运行
load("/home/yeung/tmp/load.il") ; Current path: /home/yeung/tmp/load.il
-
ycGetDirName
正则处理,从 ycGetFileName
函数结果中提取父目录的路径。
需要注意的是,如果是在 CIW 中执行 ycGetFileName
函数,它的返回值会是 *ciwInPort*
,这种情况直接用 getWorkingDir()
作为 ycGetDirName
函数的返回值。
procedure(ycGetDirName(\@optional file(ycGetFileName()) "t")
let((dir)
dir = if(pcreMatchp("^\\*.+\\*$" file)
then
getWorkingDir()
else
unless(pcreMatchp("^[~/]|^\\.{1,2}/" file)
file = strcat("./" file)
)
pcreReplace(pcreCompile("(?<=/)[^/]+/*$") file "" 0)
)
dir = simplifyFilename(dir t)
)
)
-
还是修改
/home/yeung/tmp/load.il
文件来演示:-
脚本内容
printf("Current directory: %s\n" ycGetDirName())
-
运行
load("/home/yeung/tmp/load.il") ; Current directory: /home/yeung/tmp
-
ycLoad
优化 load
函数。
预留一个 ignore
参数用来优化 loadi
函数。
procedure(ycLoad(file \@optional ignore "tg")
let((path)
path = if(pcreMatchp("^[~/]" file)
file
strcat(ycGetDirName() "/" file)
)
unless(isFile(path) && isReadable(path)
error("ycLoad: can't access file - %A" path)
)
if(ignore
loadi(path)
load(path)
)
)
)
ycLoadi
优化 loadi
函数。
同样可以忽略 load 过程中遇到的错误,打印错误消息,然后继续 load 。
procedure(ycLoadi(file "t")
ycLoad(file t)
)
效果
-
以前 load
let((home wind sync) home = "/home/yeung/script/skill/tools/" wind = list("hiForm.il" "windCtrl.il") sync = list("syncView.il" "support3rd.il") foreach(x wind load(strcat(home "src/wind/" x)) ) foreach(x sync load(strcat(home "/src/sync/" x)) ) ); let
非常啰嗦。
其次如果父文件夹改了名,或者移动了位置,还得一个个文件打开检查路径并修改,还要增加无意义的版本。
copy 给别人也不能傻瓜式操作,直接 load 报个错:*Error* load: can't access" 唉 你 这 脚 本 有 问 题 啊 ? " -
现在 load
ycLoad("src/wind/hiForm.il") ycLoad("src/wind/windCtrl.il") ycLoad("src/sync/syncView.il") ycLoad("src/sync/support3rd.il") ; END 这里最好空一行,后面会解释
"优雅地" load 完所有文件。
FAQ
-
发现一个稳定性问题,初步判断可能是 Skill 本身的 bug
ycLoad("src/sync/support3rd.il")
上面这几句加载后可能会出现找不到文件的报错,语句本身是没有问题的。
自己测试发现,piport 可能会被赋予到 “错误” 的值。
猜测原因在于,读取完最后一句ycLoad
之后,当前的文件已经没有再需要读取的内容了,又因为某些问题存在一定的延迟,此时指针可能已经退出当前这个文件,回到了上一个文件中,所以本地 virtuoso 接着运行get_filename(piport)
获取到了错误的路径。这个现象是我的脚本文件路径位于 NFS 挂载的远程存储的时候发现的,复现率极高。
解决办法:
ycLoad
函数使用不要位于最后一行ycLoad("src/sync/support3rd.il") ; 特意空一行