在 Cloudflare 的人們都非常喜歡 Go 語言。我們在許多內部軟體項目以及更大的管道系統中使用它。但是,我們能否進入下一個層次並將其用作我們最喜歡的作業系統 Linux 的腳本語言呢?
簡短點的回答:為什麼不呢?Go 相對容易學習,不冗餘並且有一個強大的生態庫,這些庫可以重複使用避免我們從頭開始編寫所有代碼。它可能帶來的一些其他潛在優勢:
咋一看 Go 腳本貌似很容易實現 Unix 腳本的 shebang(#! ...) 支持。shebang 行)是腳本的第一行,以 #! 開頭,並指定腳本解釋器用於執行腳本(例如,#!/bin/bash 或 #!/usr/bin/env python),所以無論使用何種程式語言,系統都確切知道如何執行腳本。Go 已經使用 go run 命令支持 .go 文件的類似於解釋器的調用,所以只需要添加適當的 shebang 行(#!/usr/bin/env go run)到任何的 .go 文件中,設置好文件的可執行狀態,然後就可以愉快的玩耍了。
但是,直接使用 go run 還是有問題的。這篇牛 b 的文章 https://gist.github.com/posener/73ffd326d88483df6b1cb66e8ed1e0bd 詳細描述了圍繞 go run 的所有問題和潛在解決方法,但其要點是:
package main:
helloscript.go:1:1: illegal character U+0023 '#'
這篇文章描述了上述問題的幾種解決方法,包括使用一個自定義的包裝程序 gorun 作為解釋器,但是都沒有提供一個理想的解決方案。你可以:
OK,看起來 shebang 的方法並沒有為我們提供全面的解決方案。是否還有其他方式是我們可以使用的?讓我們仔細看看 Linux 內核如何執行二進位文件。 當你嘗試執行一個二進位/腳本(或任何有可執行位設置的文件)時,你的 shell 最後只會使用 Linux execve 系統調用,將它傳遞給二進位文件系統路徑,命令行參數和 當前定義的環境變量。 然後內核負責正確解析文件並用文件中的代碼創建一個新進程。 我們中的大多數人都知道 Linux (和許多其他類 Unix 作業系統)為其可執行文件使用 ELF 二進位格式。
然而,Linux 內核開發的核心原則之一是避免任何子系統的 「vendor/format lock-in」,這是內核的一部分。因此,Linux 實現了一個「可插拔」系統,它允許內核支持任何二進位格式 - 所有你需要做的就是編寫一個正確的模塊,它可以解析你選擇的格式。如果仔細研究內核原始碼,你會發現 Linux 支持更多的二進位格式。例如,最近的4.14 Linux 內核,我們可以看到它至少支持7種二進位格式(用於各種二進位格式的樹內模塊通常在其名稱中具有 binfmt_ 前綴)。值得注意的是 binfmt_script 模塊,它負責解析上面提到的 shebang 行並在目標系統上執行腳本(並不是每個人都知道 shebang 支持實際上是在內核本身而不是在 shell 或其他守護進程/進程中實現的)。
但既然我們認為 shebang 不是 Go 腳本的最佳選擇,似乎我們需要別的東西。令人驚訝的是,Linux 內核已經有了一個「其他類型的」二進位支持模塊,它有一個貼切的名稱 binfmt_misc。該模塊允許管理員通過定義良好的 procfs 接口直接從用戶空間動態添加對各種可執行格式的支持,並且有詳細記錄。
讓我們按照文檔並嘗試為 .go 文件設置二進位格式描述。首先,該指南告訴您將特殊的 binfmt_misc 文件系統安裝到 /proc/sys/fs/binfmt_misc。如果您使用的是基於 systemd 的相對較新的 Linux 發行版,則很可能已經為您安裝了文件系統,因為默認情況下 system 會為此安裝特殊的 mount 和 automount 單元。 要仔細檢查,只需運行:
另一種方法是檢查 /proc/sys/fs/binfmt_misc 中是否有文件:正確安裝 binfmt_misc 文件系統將至少創建兩個名稱為 register 和 status 的特殊文件。
接下來,因為我們希望我們的 .go 腳本能夠正確地將退出代碼傳遞給作業系統,所以我們需要將定製的 gorun 包裝器作為我們的「解釋器」:
從技術角度上講,我們不需要將 gorun 移動到 /usr/local/bin 或任何其他系統路徑,而無論如何 binfmt_misc 都需要解釋器的完整路徑,但系統可以以任意權限運行此可執行文件,因此從安全視角來看限制文件訪問權限是一個好主意。
在這一點上,讓我們來建一個簡單的 go 腳本 helloscript.go 並驗證我們可以成功「解釋」它。腳本如下:
檢查參數傳遞和錯誤處理是否按預期工作:
現在我們需要告訴 binfmt_misc 模塊如何使用 gorun 執行 .go 文件。按照文檔中的描述我們需要配置如下字符串: :golang:E::go::/usr/local/bin/gorun:OC,意思是告訴系統:當遇到以 .go 為擴展名的可執行文件,請使用 /usr/local/bin/gorun 解釋器執行該文件。字符串末尾的 OC 標誌確保腳本將根據腳本本身設置的所有者信息和權限位執行,而不是在解釋器二進位文件上設置的那些位。這使 Go 腳本的執行行為與 Linux 中其他可執行文件和腳本的行為相同。
讓我們註冊我們新的 Go 腳本二進位格式:
如果系統成功註冊了,則應在 /proc/sys/fs/binfmt_misc 目錄下顯示新的 golang 文件。 最後,我們可以在本地執行我們的 .go 文件:
就這樣了!現在我們可以根據自己的喜好編輯 helloscript.go,並在下次執行文件時看到更改將立即可見。此外,和此前的 shebang 方式不同,我們可以隨時使用 go build 將文件編譯成真正的可執行文件。
via: https://blog.cloudflare.com/using-go-as-a-scripting-language-in-linux/
作者:Ignat Korchagin 譯者:shniu 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出