異質系統整合,呼叫外部程式永遠是最簡單粗暴的手段,另起 Process 執行第三方工具再透過參數、檔案溝通,這種「外包給第三方執行檔」的做法,不管效能、資源耗用量或回應速度都會打折扣,但重點是 - 至少程式會跑能驗收呀! 這招永遠是沒有其他辦法時的救命稻草。
(話說:我出道的第一個網站專案是為 UNIX 工作站寫網站整合 Oracle,當時我還是只會 VB/VBScript/SQL 的菜鳥,被丟包到 UNIX 星球一整個驚慌,最後靠著寫 Perl 呼叫 sqlplus user/pass@db_name @script_file 大絕,竟也混到驗收結案。延伸閱讀:黑歷史 - 我的 SQL Injection 首部曲)

之後的開發生涯,遇到比較複雜的需求,找不到合適程式庫但有現成執行檔,我也常靠這招解決,醜歸醜,但省時又省力。(例子:從 Linux 控制掃瞄器呼叫 openssl 分析憑證請 Acrobat Reader 幫忙列印...)

最近聽到一起案例,某 SUSE 作業系統上的軟體需要整合 SFTP 收發檔案,但該軟體對外溝通只提供「讀寫檔案」及「執行外部程式」兩種管道。嘿,擬好上傳下載指令再外包給 sftp CLI 不就解決了?加上環境 SUSE 是我從沒玩過的 Linux 版本,決定把它當成一道暖身練習題。

SUSE 有眾多發行版本,伺服器應用主要會採用 openSUSE Leap 或 SUSE Linux Enterprise Server (SLES),二者的關係差不多像 Fedora/CentOS Stream 與 RHEL (延伸閱讀:Linux 作業系統考察),基於相同原始碼且高度相容,前者由社群開發維護,後者則為商業軟體,有廠商提供支援。因此,要學習及研究 SUSE,裝 openSUSE Leap 就對了,若工作領域遇到 SLE,技能跟經驗值可通用。

順道一提:.NET 也支援 openSUSE 及 SLES 哦! 所以下回再有聽到有人說 .NET 只能在 Windows 上跑,記得衝上去搖晃他肩膀說「快醒醒,時代早就不一樣了」。(謎之聲:其實並不需要這樣)

本次測試我打算跑 Docker 當 SFTP Server,於是先試了在 openSUSE 上裝 Docker。官方文件寫得蠻詳細的,SUSE 的套件管理工具是 Zypper,使用上跟 Ubuntu 的 apt 一樣簡便,使用以下指令安裝 Docker,設成服務,並授與登入的使用者使用權限(未來呼叫 docker 時不需加 sudo),安裝程序就完成了:

sudo zypper install docker docker-compose docker-compose-switch
sudo usermod -G docker -a $USER
sudo systemctl enable docker
sudo systemctl start docker
newgrp docker #or logout and login
docker --version

由於只是實驗性質,我隨便找了一個 atmoz SFTP Server Docker Image,透過 Docker 參數指定帳號密碼跟資料夾就能跑,簡單省力。至於 sftp CLI 的身分認證問題,Github Copilot 教會我用 sshpass (需用 Zypper 另外安裝),可用檔案或環境變數(SSHPASS)記憶密碼(也支援明碼參數提供,但不建議),再用 sshpass -f /path/to/passwordfile ssh user@hostsshpass -e sftp user@host 間接啟動 ssh 或 sftp,便能克服密碼輸入問題。當然,改用金鑰登入 SSH/SFTP/SCP會更安全,但需要管理單位配合,密碼還是最通用的做法。

啟動 SFTP 伺服器容器(SUSE 本身 22 Port 已用於 ssh,改映對到 2234 Port),設好 sshpass,用 sshpass -e sftp -P 2234 foo@localhost 即可登入 SFTP:

docker run -p 2234:22 -d atmoz/sftp foo:pass:::upload
sftp -P 2234 foo@localhost
sudo zypper install sshpass
export SSHPASS='pass'
sshpass -e sftp -P 2234 foo@localhost

接下來,我排了一個段測試:登入 SFTP 後切換到 upload 目錄,建立 test 資料夾,上傳 hello.txt,另建 backup 資料夾,將 hello.txt 由 test 搬到 backup 下,刪除 hello.txt,刪除 test 及 backup 資料夾,測試結束。

我對 Bash 腳本不熟,但有 Github Copilot 在不用怕,我學會兩個 Bash 腳本技巧:

  1. Here Document
    概念類似 C# @"..." 或 PowerShell 的 @"、"@,允許在腳本直接指定多行文字內容,保留其中的換行、縮排,而 Bash 的做法是取一個任意識別字串作為起始跟結尾,只要它在內文不會出現造成誤判就好,我覺得EOF是不錯的選擇。
    COMMAND <<InputComesFromHERE
    ...
    ...
    ...
    InputComesFromHERE
    
    建目錄、上傳、搬檔、刪檔、刪目錄等一長串指令可寫成 Here Document 交給 sftp CLI 執行。
  2. 用 time 測量程式執行時間
    在 Bash 可用 time 指令串接要測量時間的命令,可得到 real、user、sys 三個秒數,分別代表總時間、User Mode 耗用時間、Kernel Mode 耗用時間。在腳本中,則可用 { ... } 包住一整段程式測量執行時間。

綜合以上概念,寫成 sftp-test.sh,chmod u+x sftp-test.sh 授與執行權限後可 ./sftp-test.sh 測試:

#!/bin/bash
echo "Hello World" > hello.txt
# use here document to pass commands to sftp
time {
sshpass -e sftp -P 2234 foo@localhost <<EOF
cd upload
mkdir test
cd test
put hello.txt
ls -l
cd ..
mkdir backup
rename test/hello.txt backup/hello.txt
ls -l backup
rmdir test
rm backup/hello.txt
rmdir backup
bye
EOF
}

測試成功。

若 Linux 環境無 sshpass 可用怎麼辦?Github Copilot 再教會我 UNIX 有個古老自動化控制與測試工具 - Expect,用 spawn 啟動一個新 Process 跑 sftp、ssh,接著用 expect "..." 等待讓程式回應特定內容,用 send "..." 模擬輸入操作。可想成 20 年前的 CLI 版的 RPA (Robotic Process Automation,機器人流程自動化)。

不費吹灰之力,Copilot 幫我改好 expect 版:

#!/usr/bin/env bash
echo "Hello World" > hello.txt
# Use expect to automate SFTP session
/usr/bin/expect <<EOF
# Start the expect environment
spawn sftp -oPort=2234 foo@localhost
# Wait for the password prompt
expect "password:"
# Provide the password
send "$SSHPASS\r"
# SFTP commands
expect "sftp>"
send "cd upload\r"
expect "sftp>"
send "mkdir test\r"
expect "sftp>"
send "cd test\r"
expect "sftp>"
send "put hello.txt\r"
expect "sftp>"
send "ls -l\r"
expect "sftp>"
send "cd ..\r"
expect "sftp>"
send "mkdir backup\r"
expect "sftp>"
send "rename test/hello.txt backup/hello.txt\r"
expect "sftp>"
send "ls -l backup\r"
expect "sftp>"
send "rmdir test\r"
expect "sftp>"
send "rm backup/hello.txt\r"
expect "sftp>"
send "rmdir backup\r"
expect "sftp>"
send "bye\r"
EOF

測試得到相同結果。

經過這演練,對於如何使用 Bash 操控其他 CLI 做事算是有較清楚的概念,未來在 Linux 上要執行異質系統整合,除了寫 .NET 程式跟用 PowerShell 外,再多一個就地取材的好武器。


Comments

# by Anonymous

curl https://get.docker.com | sh https://docs.docker.com/engine/install/debian/

# by Anonymous

https://rclone.org/sftp/

# by jackson2874

我要寫一個 .net 程式,怎麼入手比較快 -- 1. 縮小化要到工具列變成小圖示(icon) 2. 程式每五分鐘去讀 https://api.site 如果有資料,就浮窗起來「收到 N 筆資料」 3. 在小圖非按右鍵,程式選單會有 [立即更新]、[重新啟動]、[離開] 謝謝 提供方向

# by Jeffrey

to jackson2874, 這篇的做法應該接近你要的效果 https://blog.darkthread.net/blog/min-api-run-with-tray-icon/

# by archangelwu

sudo yum install sshpass

Post a comment