獲取與使用者 ID 關聯的使用者名的 POSIX 兼容方式
我經常想要獲取與使用者 ID 相關聯的登錄名,並且因為它已被證明是一個常見的案例,所以我決定編寫一個 shell 函式來執行此操作。雖然我主要使用 GNU/Linux 發行版,但我嘗試將我的腳本編寫為盡可能可移植,並檢查我所做的是否與 POSIX 兼容。
解析
/etc/passwd
我嘗試的第一種方法是解析
/etc/passwd
(使用awk
)。awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
但是,這種方法的問題是登錄可能不是本地的,例如,使用者身份驗證可能通過 NIS 或 LDAP。
使用
getent
命令使用
getent passwd
比解析更便攜,/etc/passwd
因為這也查詢非本地 NIS 或 LDAP 數據庫。getent passwd "$uid" | cut -d: -f1
不幸的是,
getent
POSIX 似乎沒有指定該實用程序。使用
id
命令
id
是用於獲取有關使用者身份的數據的 POSIX 標準化實用程序。BSD 和 GNU 實現接受使用者 ID 作為操作數:
這意味著它可用於列印與使用者 ID 關聯的登錄名:
id -nu "$uid"
但是,在 POSIX 中沒有指定提供使用者 ID 作為操作數;它只描述了使用登錄名作為操作數。
結合以上所有
我考慮將上述三種方法組合成如下內容:
get_username(){ uid="$1" # First try using getent getent passwd "$uid" | cut -d: -f1 || # Next try using the UID as an operand to id. id -nu "$uid" || # As a last resort, parse `/etc/passwd`. awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd }
然而,這很笨重、不優雅,而且——更重要的是——不夠健壯;如果使用者 ID 無效或不存在,它將以非零狀態退出。在我編寫一個更長更笨重的 shell 腳本來分析和儲存每個命令呼叫的退出狀態之前,我想我會在這裡問:
是否有一種更優雅、更便攜(POSIX 兼容)的方式來獲取與使用者 ID 關聯的登錄名?
一種常見的方法是測試您想要的程序是否存在並且是否可以從您的
PATH
. 例如:get_username(){ uid="$1" # First try using getent if command -v getent > /dev/null 2>&1; then getent passwd "$uid" | cut -d: -f1 # Next try using the UID as an operand to id. elif command -v id > /dev/null 2>&1 && \ id -nu "$uid" > /dev/null 2>&1; then id -nu "$uid" # Next try perl - perl's getpwuid just calls the system's C library getpwuid elif command -v perl >/dev/null 2>&1; then perl -e '@u=getpwuid($ARGV[0]); if ($u[0]) {print $u[0]} else {exit 2}' "$uid" # As a last resort, parse `/etc/passwd`. else awk -v uid="$uid" -F: ' BEGIN {ec=2}; $3 == uid {print $1; ec=0; exit 0}; END {exit ec}' /etc/passwd fi }
因為 POSIX
id
不支持 UID 參數,所以elif
for 子句id
不僅要測試是否id
在 PATH 中,還要測試它是否會無錯誤地執行。這意味著它可能會執行id
兩次,幸運的是這不會對性能產生明顯影響。也有可能同時執行id
和awk
,但對性能的影響同樣可以忽略不計。順便說一句,使用這種方法,無需儲存輸出。只有其中一個會執行,因此只有一個會列印輸出以供函式返回。