為了讓 Minecraft 可以持續的執行,我們常會使用 Tmux 等終端機工具。不過各位指令的魔術師們,是否想過可以透過腳本來與 Tmux 中的程式互動呢?
這篇文章中,我們會逐步介紹 tmux 的機制,並使用一個簡單的腳本,打造一個顯示系統時間的時鐘!
一個簡單的範例
首先像平常一樣,開啟一個 tmux session:
tmux
然後開一個新的終端機視窗,用 list-sessions 確認目前已經開啟的 session:
tmux list-sessions
tmux ls # 也可以縮寫成 ls

有趣的地方來了,我們其實可以透過 send-keys 功能,傳送 ls 指令到先前的 tmux 裡面:
tmux send-keys ls ENTER

上面指令中的 ENTER 就是按下 Enter 鍵的意思。
以下列出幾個常用的按鍵(不分大小寫):
EscapeEsc 鍵EnterEnter 鍵TabTab 鍵Delete或DC: Delete 鍵C-c: Control + cM-1: Alt + 1
想確認所有支援的按鍵可以從原始碼的 key_string_table 裡面找,組合鍵(Modifier)的部份可以看看 key_string_get_modifiers() 函式。
注:Shift 組合鍵的部份比較複雜,因為 Shift 鍵本身並不是一個通用的組合鍵(general modifier),它只能與 F1~F12 和 方向鍵搭配使用。舉例來說
S-a並不會送出大寫的A而是不存在對應的按鍵。換句話說,鍵盤上 Shift 鍵的意義,比較接近筆電鍵盤上的
Fn鍵。當我們按下Shift+a的時候,送出的並不是一組按鍵(Shift+a),而是一個按鍵訊號(A)。可以參考以下這篇討論中,nicm 的解釋: send-keys command with shift modifiers doesn’t work? · Issue #309 · tmux/tmux
使用 -t 指定 session
那如果我們開啟了兩個 tmux 呢?
send-keys 怎麼知道要送到哪裡?
它當然是不知道的。
以下這個範例,我們在左邊的終端機中,開啟兩個分頁,分別執行 tmux(開啟了兩個 tmux 的 session)。並在右邊的終端機中,送出 send-keys 指令:

結果是第二個分頁(較新的分頁)收到這個指令,這種不確定的特性可不適合寫腳本的時候使用啊!
好消息是,我們可以指定自訂的 session 名稱,並讓 send-keys 把按鍵傳送給它!
send-keys [-HlMRX] [-N repeat-count] [-t target-pane] key ...
(alias: send)
首先請改用 new-session 指令,並加上 -s 參數指定 session 名稱(本例為 mySession):
tmux new-session -s mySession
透過 list-sessions 指令,我們可以發現剛才的 1 號 session 的位置,這次的名子是 mySession:

透過 -t 選項,我們可以指定把 ls 指令送到 mySession 裡面:
tmux send-keys ls ENTER # 送到最新的 session
tmux send-keys -t mySession ls ENTER # 送到 mySession

注:在本文寫作的當下
send-keys指令在-t 0的時候,判斷會失準,導致按鍵被送到錯誤的 session,如果你也遇到同樣的問題,可以用以下指令重新命名 session:tmux send-keys -t 0 "t0" ENTER
較為複雜的 session
這邊為了方便理解,我們開一個乾淨的 session 來解釋。
首先開啟一個新的 session:
tmux new-session -s mySession
並且開啟複數的 windows 和 panel,第一頁放了 Minecraft Server 和 htop 等工具,第二頁則是一個 shell,方便進行設定檔的修改。

注:tmux 預設按鍵綁定如下(部份):
- 按下
Control+b之後的按鍵視為 tmux 命令(稱為prefix)prefix+%垂直分割 window (左右各半)prefix+"水平分割 window (上下各半)prefix+$重新命名 sessionprefix+,重新命名 window
將第一個 window 重新命名為 server_dash,第二個 window 重新命名為 files:
tmux rename-window -t mySession:0 server_dash
tmux rename-window -t mySession:1 files
可以從終端機下方的狀態列看出改變:

舉例來說,假設我們要 傳入指令到 Minecraft Server ,可以透過以下的指令來達成:
tmux send-keys -t mySession:server_dash.0 list ENTER
意思是要重送到 mySession 的 server_dash 的 第 0 個 panel 中。

一個範例:現實時間的時鐘
這邊使用一個橡木告示牌顯示時間,透過 Server 終端機中執行 setblock 指令,將外部的時間傳入:
setblock 242 67 -96 oak_sign[rotation=8]{Text1:'',Text2:'"Time"',Text3:'"01:01"'}
242 67 -96是方塊的座標 X/Y/Zoak_sign指定橡木告示牌[rotation=8]因為告示牌的文字只有在其中一面,所以把它轉過來{Text1:'',Text2:'"Time"',Text3:'"01:01"'}告示排上的文字,第一行空白
有時
setblock不能重複指定相同的方塊,會出現如下的錯誤:[Server thread/INFO]: Could not set the block所以更新時間前必須先設定該方塊為空氣:
setblock 242 67 -96 air
編寫成腳本:
#!/bin/bash
while true; do
# 設為空氣
tmux send-keys -t mySession:server_dash.0 "setblock 242 67 -96 air" ENTER
# 設為告示牌
tmux send-keys -t mySession:server_dash.0 "setblock 242 67 -96 oak_sign[rotation=8]{Text1:'',Text2:'\"Time\"',Text3:'\"$(date "+%X")\"'}" ENTER
# 暫停一秒
sleep 1
done
加上執行權限:
chmod u+x sys_time.sh
執行之後會看到這個告示牌不斷的顯示最新的時間了!

注:如果畫面上的文字輸出很煩人的話,可以關掉
sendCommandFeedback
/gamerule sendCommandFeedback false
參考資料:
- python – How to get stdout and stderr from a tmux session? – Stack Overflow
- soupso 的回答提供了最基本的 tmux 指令,並敘述了如何與 Python 腳本互動。
- keyboard shortcuts – Tmux commands: What is M-whatever – Unix & Linux Stack Exchange
- 抱歉,我之前真的不認識什麼是 meta key
- date command in Linux with examples – GeeksforGeeks
- 參考了
date指令的格式化輸出
- 參考了
- 指令/setblock – Minecraft Wiki,最詳細的官方Minecraft百科
- 確認了了為什麼不能重複
setblock相同的方塊
- 確認了了為什麼不能重複
- 遊戲規則 – Minecraft Wiki,最詳細的官方Minecraft百科
- 關於各種
gamerule的解釋,當然也包括sendCommandFeedback
- 關於各種
