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") ; 特意空一行