為了讓 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 鍵的意思。
以下列出幾個常用的按鍵(不分大小寫):
Escape
Esc 鍵Enter
Enter 鍵Tab
Tab 鍵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
- 關於各種