Flask運用Xterm實現(xiàn)交互終端的示例詳解
前言
Xterm是一個基于X Window System的終端仿真器(Terminal Emulator)。Xterm最初由MIT開發(fā),它允許用戶在X Window環(huán)境下運行文本終端程序。Xterm提供了一個圖形界面終端,使用戶能夠在圖形桌面環(huán)境中運行命令行程序。而xterm.js是一個用于在瀏覽器中實現(xiàn)終端仿真的JavaScript庫。它允許在Web頁面中創(chuàng)建交互式的終端界面,用戶可以在瀏覽器中運行命令行程序,執(zhí)行命令,并與終端進行交互。
主要特點和功能包括:
- 終端仿真: xterm.js通過JavaScript模擬了一個終端環(huán)境,支持常見的終端功能,包括光標移動、顏色控制、滾動等。
- 多平臺支持: 由于是基于JavaScript實現(xiàn),xterm.js可以在各種現(xiàn)代瀏覽器上運行,無論是在桌面還是移動設備上。
- 自定義外觀: xterm.js提供了豐富的配置選項,用戶可以定制終端的外觀和行為,包括顏色、字體、光標樣式等。
- 剪貼板支持: 支持從終端復制文本到剪貼板,并從剪貼板粘貼文本到終端。
- WebSockets和其他集成: 可以與WebSockets等通信協(xié)議集成,以便在瀏覽器中實現(xiàn)實時的終端交互。
- 支持Unicode和UTF-8: 能夠正確顯示和處理Unicode字符,支持UTF-8編碼。
xterm.js通常被用于Web應用程序中,尤其是在需要提供命令行界面的場景下,如在線終端、遠程服務器管理等。這使得開發(fā)者能夠在瀏覽器中實現(xiàn)類似于本地終端的交互體驗,而無需使用本地終端模擬器。
AJAX 實現(xiàn)Web交互
AJAX(Asynchronous JavaScript and XML)是一種用于在Web應用程序中實現(xiàn)異步數(shù)據(jù)交換的技術(shù)。它允許在不重新加載整個頁面的情況下,通過在后臺與服務器進行小規(guī)模的數(shù)據(jù)交換,實現(xiàn)動態(tài)更新網(wǎng)頁內(nèi)容的目的。AJAX廣泛用于創(chuàng)建交互性強、用戶體驗良好的Web應用程序,例如在加載新數(shù)據(jù)、進行表單驗證、實現(xiàn)自動完成搜索等方面。
如下前端部分,通過使用ajax
向后端提交數(shù)據(jù),當success:function
接收到數(shù)據(jù)后直接將數(shù)據(jù)動態(tài)回寫到Xterm
終端上,代碼如下所示;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> <script type="text/javascript" src="https://www.lyshark.com/javascript/xterm/xterm.js"></script> <script type="text/javascript" src="https://www.lyshark.com/javascript/jquery/3.5.1/jquery.min.js"></script> </head> <body> <script type="text/javascript"> var window_width = $(window).width()-200; var window_height = $(window).height()-300; var term = new Terminal( { cols: Math.floor(window_width/9), rows: Math.floor(window_height/20), useStyle:false, convertEol: true, cursorBlink:false, cursorStyle:null, rendererType: "canvas", } ); term.open(document.getElementById('terminal')); function show(){ var address = $("#address").val(); var command = $("#command").val(); console.log(command); $.ajax({ url:"/", type:"POST", contentType:"application/json;", data: JSON.stringify({"address":address,"command":command}), success:function (res) { // term.clear(); term.writeln( "\x1B[1;3;33m IP地址: \x1B[0m" + res.address ); term.writeln( "\x1B[1;3;34m 命令: \x1B[0m" + res.command ); } }); } </script> <!--提交數(shù)據(jù)--> <div id="terminal"></div> <input type="text" id="address" placeholder="主機地址"/> <input type="text" id="command" placeholder="執(zhí)行命令"/> <input type="button" value="執(zhí)行命令" onclick="show()"> </div> </body> </html>
后端部分的實現(xiàn)很簡單,首先封裝一個ssh_shell
用于執(zhí)行命令,用戶傳入數(shù)據(jù)后,直接執(zhí)行并將返回結(jié)果放入到ref內(nèi)即可。
from flask import Flask,render_template,request from flask import jsonify import paramiko app = Flask(__name__) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) def ssh_shell(address,username,password,port,command): ssh.connect(address,port=port,username=username,password=password) stdin, stdout, stderr = ssh.exec_command(command) result = stdout.read() if not result: result=stderr.read() ssh.close() return result.decode() @app.route('/', methods=[ 'GET', 'POST']) def index(): if request.method == "POST": # 接收數(shù)據(jù) json_value = request.get_json() ref = ssh_shell("192.168.150.128","root","123123","22",json_value["command"]) # 發(fā)送數(shù)據(jù) info = dict() info["address"] = json_value["address"] info["command"] = ref return jsonify(info) else: return render_template("index.html") if __name__ == '__main__': app.run()
AJAX實現(xiàn)Web終端
繼續(xù)擴展將編輯框去掉,用戶輸入數(shù)據(jù)后直接傳入到Xterm內(nèi),Xterm里賣弄判斷如果出現(xiàn)了回車,則像后端發(fā)送ajax數(shù)據(jù),否則繼續(xù)偵聽并記下輸入數(shù)據(jù)。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> <script type="text/javascript" src="https://www.lyshark.com/javascript/xterm/xterm.js"></script> <script type="text/javascript" src="https://www.lyshark.com/javascript/jquery/3.5.1/jquery.min.js"></script> </head> <body> <div id="terminal"></div> <script type="text/javascript"> var window_width = $(window).width()-500; var window_height = $(window).height()-300; var term = new Terminal ( { cols: Math.floor(window_width/9), rows: Math.floor(window_height/20), useStyle:false, convertEol: true, cursorBlink: true, //光標閃爍 cursorStyle: "underline", //光標樣式 rendererType: "canvas", } ); term.open(document.getElementById('terminal')); term.writeln("welcome to lyshark web terminal!"); term.write("[shell] # "); let input = ''; term.on('key', (key, ev) => { let code = key.charCodeAt(0); console.log(code); // 如果按下回車,則發(fā)送命令,并等待輸出結(jié)果 if(code == 13) { term.write("\r\n"); $.ajax({ url:"/", type:"POST", contentType:"application/json;", data: JSON.stringify({"command": input}), success:function (res) { term.write(res.value); } }); input =''; } // 如果是退格,則清除 else if(code == 127) { term.write("\b"); } else { input += key term.write(key); } }); </script> </body> </html>
后端收到數(shù)據(jù)后解析命令,比對命令是否存在,根據(jù)不同的命令執(zhí)行不同的分支。
from flask import Flask,render_template,request from flask import jsonify app = Flask(__name__) @app.route('/', methods=[ 'GET', 'POST']) def index(): if request.method == "POST": # 接收數(shù)據(jù) json_value = request.get_json()["command"] if len(json_value) != 0: # 判斷使用哪一個分支 splite_value = json_value.split(" ") info = dict() if splite_value[0] == "help": info["value"] = "version 1.0" info["value"] = info["value"] + "\n[shell] # " return jsonify(info) elif splite_value[0] == "GetCPU": address = splite_value[1] info["value"] = "192.168.1 CPU 10%" info["value"] = info["value"] + "\n[shell] # " return jsonify(info) else: info["value"] = "命令不存在" info["value"] = info["value"] + "\n[shell] # " return jsonify(info) else: info = dict() info["value"] = "[shell] # " return jsonify(info) else: return render_template("index.html") if __name__ == '__main__': app.run()
運行后可輸出一個交互式WebShell環(huán)境,如下圖所示;
WebSocket 實現(xiàn)終端
雖然WebSSH可以方便管理主機,但如果需要批量運維則需要開發(fā)一個可以多條消息共同推送的命令行。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> <script type="text/javascript" src="https://www.lyshark.com/javascript/xterm/xterm.js"></script> <script type="text/javascript" src="https://www.lyshark.com/javascript/jquery/3.5.1/jquery.min.js"></script> <script type="text/javascript" src="https://www.lyshark.com/javascript/socket.io/socket.io.min.js"></script> </head> <body> <div id="terminal"></div> <script type="text/javascript" charset="UTF-8"> $(document).ready(function() { namespace = '/Socket'; var socket = io.connect("http://" + document.domain + ":" + location.port + namespace); var window_width = $(window).width()-500; var window_height = $(window).height()-300; var term = new Terminal ( { cols: Math.floor(window_width/9), rows: Math.floor(window_height/20), useStyle:false, convertEol: true, cursorBlink: true, rendererType: "canvas", } ); // 打開Web終端 term.open(document.getElementById('terminal')); term.write("[shell] # "); let input_command = ''; term.on('key', (key, ev) => { let code = key.charCodeAt(0); console.log(code); // 如果按下回車,則發(fā)送命令,并等待輸出結(jié)果 if(code == 13) { // 發(fā)送數(shù)據(jù)到后端 term.write("\r\n"); socket.emit("message",{"command": input_command}); input_command =''; } // 如果是退格,則清除 else if(code == 127) { term.write("\b"); } else { input_command += key term.write(key); } }); // 接受后臺返回并輸出 socket.on('response', function(recv) { console.log(recv.value); term.write(recv.value); }); }); </script> </body> </html>
后臺接收參數(shù),并更具不同的參數(shù)執(zhí)行不同的運維函數(shù),此處只做演示,具體功能需要自行編寫。
from flask import Flask,render_template,request from flask_socketio import SocketIO async_mode = None app = Flask(__name__) app.config['SECRET_KEY'] = "lyshark" socketio = SocketIO(app) @app.route("/") def index(): return render_template("index.html") # 出現(xiàn)消息后,率先執(zhí)行此處 @socketio.on("message",namespace="/Socket") def socket(message): print("接收到消息:",message['command']) command = message['command'] if len(command) != 0: splite_command = command.split(" ") if splite_command[0] == "help": socketio.emit("response", {"value": "version 1.0 \n"}, namespace="/Socket") elif splite_command[0] == "Ping": if len(splite_command) == 2: index = splite_command[1] for each in range(int(index)): socketio.sleep(0.1) socketio.emit("response",{"value": str(each) + "\n"}, namespace="/Socket") socketio.emit("response", {"value": "\n[shell] # "}, namespace="/Socket") else: socketio.emit("response", {"value": "lyShell: command not found \n"}, namespace="/Socket") else: socketio.emit("response", {"value": "[shell] # "}, namespace="/Socket") # 當websocket連接成功時,自動觸發(fā)connect默認方法 @socketio.on("connect",namespace="/Socket") def connect(): print("鏈接建立成功..") # 當websocket連接失敗時,自動觸發(fā)disconnect默認方法 @socketio.on("disconnect",namespace="/Socket") def disconnect(): print("鏈接建立失敗..") if __name__ == '__main__': socketio.run(app,debug=True)
Socket版本的將會更流暢,如下圖所示;
以上就是Flask運用Xterm實現(xiàn)交互終端的示例詳解的詳細內(nèi)容,更多關于Flask Xterm交互終端的資料請關注腳本之家其它相關文章!
相關文章
Python調(diào)用Orator?ORM進行數(shù)據(jù)庫操作
Orator?ORM?是一個功能豐富且靈活的?Python?ORM庫,旨在簡化數(shù)據(jù)庫操作,它支持多種數(shù)據(jù)庫并提供了簡潔且直觀的?API,下面我們就來看看它的具體使用吧2025-02-02PyTorch中g(shù)rid_sample的使用及說明
這篇文章主要介紹了PyTorch中g(shù)rid_sample的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02python 如何用 Hypothesis 來自動化單元測試
這篇文章主要介紹了python 如何用 Hypothesis 來自動化單元測試,幫助大家更好的理解和學習使用python,感興趣的朋友可以了解下2021-03-03Python中在for循環(huán)中嵌套使用if和else語句的技巧
Python的語法糖非常強大,比如Python中在for循環(huán)中嵌套使用if和else語句的技巧便十分給力,下面我們就舉幾個例子來看詳細的用法:2016-06-06