大多數終端模擬器會自動偵測螢幕上出現的 URL,並允許使用者方便地開啟它們(例如透過 Ctrl+點擊、Cmd+點擊或右鍵選單)。
然而,直到現在,任意文字指向 URL 的功能仍無法實現,就像網頁上的超連結一樣。
2017 年春季,GNOME Terminal 和 iTerm2 開始支援此功能。
GNOME Terminal 基於 VTE 元件,幾乎所有相關工作都集中在 VTE 上。因此,我們預期其他基於 VTE 的終端模擬器也會很快跟進並加入支援。其他終端模擬器也歡迎並鼓勵加入此行列!
以下是一個簡單指令用來測試此功能。結果等同於這個 HTML 連結:This is a link
支援此功能的終端模擬器與基於終端的應用程式清單不再由此維護,而是由 Alhadis 在其「OSC 8 在終端模擬器中的採用」頁面中維護。
我們有幾個應用場景:
apt-changelog 可以自動將錯誤 ID 格式化為指向錯誤追蹤頁面的連結。
git log 或其他類似工具可以將提交 ID 轉為指向代碼庫網頁前端的連結。
檔案瀏覽器與編輯器可以自動偵測文件中的 URI,並將其轉換為超連結,即使它們只部分顯示在螢幕上。例如一個虛構的文字編輯器同時開啟兩個檔案的截圖:
使用 Ctrl+點擊 http://exa 或 le.com 的任意位置,都能開啟 http://example.com 網頁。
核心工具如 ls、find 也可以選擇性地將列印的檔案標示為 file://... URI,讓使用者只需點擊即可在圖形應用程式中開啟。
我們希望 less -R 能識別並處理此逃脫序列,就像處理顏色一樣,這樣管線輸出到 less -R 時,超連結仍能正常運作。
超連結會在遇到帶有目標 URI 的 OSC 8 逃脫序列時開啟。語法如下:
接著,所有後續繪製的字元格都會成為指向此目標的超連結。超連結以相同的逃脫序列關閉,但省略參數與 URI,只保留分隔符:
OSC(作業系統命令)通常是 ESC ]。
params 是可選的 key=value 列表,以冒號 : 分隔。例如 id=xyz123:foo=bar:baz=quux。目前僅定義 id 鍵,參數允許未來擴展。通常不定義參數時,兩個分號必須連續出現。
URI 是超連結目標,需 URI 編碼。網頁地址需以 http:// 或 https:// 開頭。FTP 用 ftp://,本地檔案用 file://(見下方主機名稱說明),電子郵件用 mailto: 等。終端模擬器決定支援哪些協議及啟動哪些應用程式。
序列以 ST(字串終止符)結束,通常是 ESC \。(雖然根據 ECMA-48 §8.3.89,ST 是標準序列,但常見使用 BEL(\a)字元結束,這是 XTerm 的非標準做法,後被多數終端模擬器採用。仍鼓勵使用標準 ST。)
(OSC 與 ST 也有 C1 變體,可能在某些情境支援。8 位元 Latin-X 字元集為單字節 0x9d 與 0x9c。UTF-8 模式下,有些終端模擬器不支援 C1,因為會與 UTF-8 編碼衝突;另一些則識別 U+009d(0xc2 0x9d)與 U+009c(0xc2 0x9c)。由於 C1 在現今 UTF-8 編碼中非普遍支援,建議避免使用。)
此功能仿造網頁錨點,但因終端模擬器特性有所不同。
HTML 頁面應包含平衡且不嵌套的 <a ...> 與 </a> 標籤對,以建立 DOM 樹。終端模擬器無此概念,而是狀態機,串流解讀資料。
因此,OSC 8 逃脫序列在終端模擬器中只是改變超連結(或無超連結)狀態。可合法地從一個超連結切換到另一個,無需明確關閉前者。也可合法關閉未開啟的超連結(例如確保應用程式異常退出後清理)。
可將超連結視為字元格的另一屬性,類似前景色、背景色、粗體、斜體、刪除線等。切換顏色時無需重置,超連結亦同理。
網頁瀏覽器、桌面環境等通常忽略 file://hostname/path/to/file.txt URI 的主機名稱部分。終端模擬器若忽略,SSH 遠端使用時會導致目標錯誤。因此不允許此種疏忽。列印超連結的工具應填寫主機名稱,終端模擬器應比對本地主機名稱,若不符則拒絕開啟檔案(或提供其他選項,如 iTerm2 的 scp 下載)。
RFC 8089 指出主機名稱應為完全限定名稱,Freedesktop 的 File URI 規範則要求使用 gethostname() 回傳值,通常非完全限定。簡單工具不應負責主機名稱解析,建議使用 gethostname()(shell 腳本可用 $HOSTNAME)。
終端模擬器應比對本地主機名稱,可能接受多種名稱(短名與完全限定名),但可只用 gethostname()。也必須接受 localhost 或空字串為本地主機。若主機名稱不同,則不得開啟同名本地檔案。
許多終端模擬器會自動識別螢幕上的網頁地址,並在滑鼠懸停時加底線。為提供類似使用體驗與易發現性,我們也計畫在此功能中實現相同效果。
但有個不簡單的問題:滑鼠懸停時該為哪些字元加底線?與網頁不同,我們缺乏語意資訊,無法知道哪些字元屬於同一錨點。
回想前述 http://exa 與 le.com 範例?為使超連結功能完整,虛構文字編輯器應能指定這兩段文字屬於同一錨點,滑鼠懸停任一段時,兩者皆加底線。
為此,引入了小寫 id 參數,將字元連結起來。
具有相同目標 URI 且非空 id 的字元格,在滑鼠懸停時會一起加底線。
相同 id 僅用於連結相同 URI 的字元格。指向不同 URI 的字元格不應同時加底線。
沒有 id(或空 id)的超連結字元格,終端模擬器會用啟發式方法判斷哪些字元屬於同一連結。VTE 與 iTerm2 在此有所不同,但實務上差異不大。(VTE 遇到無 id 的 OSC 8 URI 時會自動分配唯一 id,連結同一 OSC 8 期間列印的字元;iTerm2 則根據螢幕內容,連結相鄰且無 id 但指向相同 URI 的字元。)
實作超連結但不想滑鼠懸停加底線的終端模擬器,可忽略 id 參數。
應用程式該如何使用?以下為粗略指引,實際可依需求調整。
簡單工具「僅」列印標準輸出時,不應指定 id,功能會正常運作。
複雜應用程式管理全螢幕且需明確連結 URI(如瀏覽器或編輯器),應指定明確 id,確保跨換行、跨視窗面板、甚至優化重繪時,連結仍維持底線。此 id 可為檔案偏移量或超連結起始的(列,欄)座標。支援多視窗的應用程式,應將視窗 ID 加入 id,避免不同視窗中相同目標 URI 衝突。
顯示可能含有 OSC 8 超連結資料的複雜應用(如終端多工器、less -R)應:
若遇到已有 id 的 OSC 8 超連結,應加上靜態字串前綴,或多視窗/面板時加上唯一前綴,避免衝突。
若遇無 id 的 OSC 8 超連結,應自動產生 id,以支援多視窗/面板及部分重繪,保持語意連結。此 id 應來自不會與明確 id 衝突的命名空間。實作上可採用 VTE 方法:每遇無 id 的 OSC 8 URI,自動分配新 id(例如遞增整數),無需維護內部 id 池。
未切換至終端模擬器「替代螢幕」的全螢幕應用(即退出後內容仍留在螢幕),應在 id 加入其他識別碼(如進程 ID 或隨機數),避免使用者滾動歷史時,前兩次會話殘留衝突。
目前無法偵測終端模擬器是否支援超連結,未來希望能解決此問題。
超連結功能應用於提供快速方便的目標 URI 存取,但(至少預設)不應是唯一判斷目標的方法。
任何正確實作 ECMA-48 OSC 解析的終端,保證不會有相容性問題。即使不支援明確超連結,目標 URI 會被靜默忽略,僅顯示預期文字,無異常。
若終端在 OSC 8 超連結序列時顯示錯誤,該終端即為 ECMA-48 規範外的錯誤實作,規範不負責處理此類錯誤終端。
目前已知有錯誤(OSC 8 導致顯示破壞)的終端包括 VTE 0.46.2 與 0.48.1 版本、Windows Terminal 0.9 以下、Emacs 內建終端,以及 screen 處理 700 字元以上 URL 時。
終端模擬器傳統上每字元格使用約十幾字節,加入超連結後可能大幅增加記憶體使用,實作上較為棘手,通常會設置安全限制。
VTE 與 iTerm2 對 URI 長度限制為 2083 字節。無正式標準限制,實務上約 2000 字元,Internet Explorer 支援 2083。
VTE 對 id 限制為 250 字節,可能變動,不應依賴此數字。工具應遠低於此限制,以便中間軟體層添加前綴仍安全。iTerm2 對 id 長度無限制。
終端模擬器可能對整體 OSC 8 逃脫序列長度設最大限制。
VTE 計畫在寫入過多長連結至滾動緩衝區時,故意稍微放慢速度,防止惡意應用快速耗盡緩衝空間,正常使用不受影響。此功能尚未實作。
為了可攜性,參數與 URI 不得包含 32–126 範圍外的字節,否則行為未定。URI 中範圍外字節必須 URI 編碼。
語法限制參數值不能包含 : 與 ; 字元,未來擴展可用 URI 編碼或 base64 轉義。
若未來擴展支援懸停顏色或屬性,建議考慮使用 ANSI 顏色與屬性標記法。
此功能並未引入網頁瀏覽已有的安全風險,因此認為無需擔心安全問題。
此外,無「Referer」洩漏問題。
不過,有幾點值得注意:
部分本地應用可能註冊自訂 URI 協議(如 foobar://),若 URI 惡意製作,處理程式可能有漏洞。終端模擬器可選擇白名單知名協議,對不明協議要求使用者確認。
有人擔心此功能對使用者來說是意外行為,且自動將終端與瀏覽器連結,可能違反「深度防禦」概念。多步驟攻擊可能利用網站漏洞,透過社交工程誘使使用者點擊終端中收到的連結。此規範不負責此類情境,只負責直接安全漏洞。
終端模擬器可考慮以下防禦措施:
不應在單純點擊時開啟連結(通常用於複製或報告滑鼠事件),而應在複雜操作如 Ctrl+點擊或右鍵選單時開啟。
應提前告知使用者 URI。
可提供開啟前確認對話框。
可提供關閉此功能選項,甚至預設關閉。
重要環境的使用者或系統管理員可選擇完全停用此功能。