iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Software Development

在麥塊的農場裡寫 Lua系列 第 9

Day9 自訂開機執行的程式碼 - 函數宣告與語法糖

前面幾天,我探索了 Lua 的變數型別、條件判斷、迴圈、標準函式庫等
在這過程中,我已經多少看過函數定義和使用了
就算沒學過,看多了總會模仿阿

今天我就來正式的研究 Lua 函數怎麼寫
主題則是,自定義 CC: Tweaked 電腦開機時執行的程式碼!
之前我一直在研究開機時自動執行的 motd.lua
這次我想要讓電腦執行自己寫的 Lua 程式
挖掘開始!

CC: Tweaked 電腦是如何讓 startup.lua 自動執行?

首先,我已經知道開機時會自動執行 startup.lua
從這個線索往上溯源,可以找到 /rom/programs/shell.lua #591 有這一行

shell.run("/rom/startup.lua")

再繼續往上東翻西找,可以找到 /rom/bios.lua #977 有 Run the shell
這部分先不深入
但可以看出 startup.lua 被自動執行的脈絡

/rom/startup.lua 理論上是不能修改的,那我要怎麼自定義呢?
我東翻西找 startup 關鍵字,先看到 bios.lua #864 有這一段

-- Set default settings
settings.define("shell.allow_startup", {
    default = true,
    description = "Run startup files when the computer turns on.",
    type = "boolean",
})
settings.define("shell.allow_disk_startup", {
    default = commands == nil,
    description = "Run startup files from disk drives when the computer turns on.",
    type = "boolean",
})

看來就是設定是否允許跑 startup
接著搜尋 allow_startupallow_disk_startup
可以看到 startup.lua #161 有這一段 Run the user created startup

-- Run the user created startup, either from disk drives or the root
local tUserStartups = nil
if settings.get("shell.allow_startup") then
    tUserStartups = findStartups("/")
end
---------- 略 ----------
if tUserStartups then
    for _, v in pairs(tUserStartups) do
        shell.run(v)
    end
end

答案呼之欲出了!我相信上面這一段有我要的東西~

Lua 函數定義,function 也是變數

來看看 findStartups 的函數定義

local function findStartups(sBaseDir)
    local tStartups = nil
    local sBasePath = "/" .. fs.combine(sBaseDir, "startup")
    local sStartupNode = shell.resolveProgram(sBasePath)
    if sStartupNode then
        tStartups = { sStartupNode }
    end
    -- It's possible that there is a startup directory and a startup.lua file, so this has to be
    -- executed even if a file has already been found.
    if fs.isDir(sBasePath) then
        if tStartups == nil then
            tStartups = {}
        end
        for _, v in pairs(fs.list(sBasePath)) do
            local sPath = "/" .. fs.combine(sBasePath, v)
            if not fs.isDir(sPath) then
                tStartups[#tStartups + 1] = sPath
            end
        end
    end
    return tStartups
end

這樣的函數定義,似乎跟其他語言大同小異,沒什麼好說的 ....
是這樣嗎?!
事實上,function 在 lua 世界裡面也是變數,型別就是 function!
如果忘記了,可以複習一下 Lua 變數型別與宣告

Lua 語法糖

事實上,大家所看到的上面這一段宣告,只是 Lua 的語法糖

function findStartups(sBaseDir)
end

它原本的寫法是這樣

findStartups = function (sBaseDir)
end

如果再繼續找 shell.resolveProgram() 會看到 command:find("/")
這也是 Lua 的語法糖
原本的寫法是

string.find(command, "/")

還有一種寫法,我感到很陌生徬徨@@... 一時之間沒有了解,先筆記下來

string["find"](command, "/")

宣告區域變數/函數

至於 local 則是限制這個變數的存取範圍
以上述為例,在 startup.lua 宣告 local,就是只能在 startup.lua 使用
否則任何地方都可以使用,這就是 Lua 的語言特性
任何變數沒有宣告 local 都是全域變數

自定義開機 startup script

回到一開始我想解決的問題,自定義開機程式
shell.resolveProgram(sBasePath) 會去找到 /startup.lua 和 /rom/startup.lua
所以只要自己新增 /startup.lua 即可在開機時自動執行
如果想要一次跑多個 Lua 程式檔
也可以新增多個檔案在 /startup/ 資料夾底下

今天先挖礦到這,下次我預計仍是會繼續研究 function


上一篇
Day8 開機學習 Lua - 迴圈控制、迭代函數
下一篇
Day10 為什麼電腦懂我的指令?函數宣告 part2
系列文
在麥塊的農場裡寫 Lua30

尚未有邦友留言

立即登入留言