關於在 bash 中檢查所需指令和介面的討論
大家好,我是無能。
結束了在公司的最後一個工作日,收到了同公司女孩送的巧克力,在離職前不久被帶去吃了烤肉,最後和一位做了25年網路工作的人,聊了聊他過去和現在的家庭網路環境,我也聊了聊我自己的家庭伺服器相關的事情,度過了愉快的時光。
我認為我已經盡力做到了我這個不成熟的人所能做的事情,但我還有很多不足之處,對此感到抱歉。
我每天都感受到 shell script 的強大。
當然,其可移植性較低,確實無法像目前 FreeBSD 預設 shell csh 那樣進行函數式編寫,這個問題是不可否認的。
然而,我認為 shell script 實際上在於其介面處理的簡易性。
C 語言與 Shell Script 的介面
例如,假設在 shell script 中執行以下 echo 指令。
echo "test"
這相當於 C 語言中的標準輸出。
#include <stdio.h>
int main() {
const char *message = "test";
// 將訊息輸出到標準輸出
fprintf(stdout, "%s\n", message);
return 0;
}
如果想輸出到標準錯誤輸出,則進行如下重定向:
echo "testerr" 1>&2
而在 C 語言中,則相當於以下程式碼。
#include <stdio.h>
int main() {
const char *message = "testerr";
// 將訊息輸出到標準錯誤輸出
fprintf(stderr, "%s\n", message);
return 0;
}
而且,大多數 GNU/Linux 指令執行失敗時的回傳值,由於過去開發者的精心設計,使得判斷條件的測試變得容易,如果將其視為腳本語言來使用,將會非常有效。
然而問題是,這只有在可以使用 shell script 的環境下才能受益。
即使以其他語言編寫類似的程式碼,其通用性仍然有效,但指令在某種程度上是作為語言中的函式庫機制運作的,我認為這也是其通用性較差的原因。
然而,我認為能夠如此簡潔地操作檔案描述符是非常強大的。
else 也只需連接兩個管道。
※寫完後才想到,嚴格來說這並不是 else 呢 ^^;
它只是在回傳值不為 0 時,執行類似 else 的操作。
$ curl aa || echo "wtf"
curl: (6) Could not resolve host: aa
wtf
如果我在國中時期就知道這麼方便的東西,那該有多好啊。
我希望任何網站都能提供更多學習 shell script 的機會。
話說回來,現在回想起來,當時 GNU/Linux 的日文環境相當困難,日文輸入的預測轉換也達不到實用水平,所以現在的便利性或許是因為 Chrome OS 的普及,使得 OSS 日文輸入法得以蓬勃發展。
我記得在國中時期,有個古怪的朋友想要一台安裝了 Ubuntu 的 ThinkPad,然後是我幫他設定的...
那麼,如何檢查指令呢?
在 Bash shell script 中編寫指令檢查時,我一直在想有沒有更簡潔易讀的寫法。
話雖如此,將原本就很簡潔的陣列透過 for 迴圈傳遞當然很方便,但看看陣列儲存時的寫法。
#!/bin/bash
cmds=(aa bb cc)
for i in ${cmds[@]}; do
command -v $i 1> /dev/null || echo "Please install $i"
done
使用 () 將指令作為陣列儲存到 cmds 中,並使用 ${cmds[@]} 展開陣列。
但這樣一來,如果指令增加,每次都要在 cmds=() 中添加,就會讓人覺得真的有必要寫在這裡嗎?
在其他語言中,依賴關係的資訊通常會集中在 Cargo.toml、deno.json、go.mod、Gemfile,以及常被嫌棄的 requirements.txt 等檔案中。
如果能有一個像那樣彙整的檔案,應該會很方便。
因此,我嘗試了這樣的方式。
#!/bin/bash
cat ./cmd | xargs which 1> /dev/null || echo "Please install cmd"
那麼,cmd 檔案會是什麼樣子呢?
$ cat cmd
ls
pwd
cargo
其實我本來想用 command 指令來檢查,但 command 是作為 shell 的內部指令運作,無法傳遞給 xargs。
$ which command
which: no command in (/home/haturatu/.rbenv/bin:/home/haturatu/.rbenv/shims:/home/haturatu/.deno/bin:/home/haturatu/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/opt/android-sdk/platform-tools:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/lib/plan9/bin:/home/haturatu/.local/bin:/home/haturatu/.local/bin:/home/haturatu/Android/Sdk/cmdline-tools/tools/bin:/home/haturatu/Android/Sdk/platform-tools:/home/haturatu/Android/Sdk/emulator:/home/haturatu/Android/Sdk/tools:/home/haturatu/Android/Sdk/tools/bin:/home/haturatu/Android/Sdk/cmdline-tools/tools/bin:/home/haturatu/Android/Sdk/platform-tools:/home/haturatu/Android/Sdk/emulator:/home/haturatu/Android/Sdk/tools:/home/haturatu/Android/Sdk/tools/bin:/home/haturatu/.npm-global/bin:/home/haturatu/go/bin)
如果我理解錯了,很抱歉,但我認為當透過 xargs 傳遞時,它會作為一個獨立於執行 bash 程序的 xargs 程序運行,因此無法識別。
因此,雖然無法將 which 失敗的指令傳遞給 Please install cmd,但在那之前 which 的錯誤輸出會顯示,所以我認為這不是必需的。
※寫完後才想到,如果用 xargs /bin/bash command -v 的話或許可以...
將這些指令群組放在另一個檔案中還有一個好處。
可以從這個檔案一次性安裝。
cat cmd | xargs -I {} sudo pacman -S --noconfirm {}
對於 apt,也可以使用 -y 選項來實現。
總之,姑且不論我是否真的會使用,我將其作為一個想法提出。
那麼,下次再見。請多指教。(從年終總結的文章開始,不知不覺寫了好幾篇文章,這一定是我的錯覺。)