Users

獲取與使用者 ID 關聯的使用者名的 POSIX 兼容方式

  • September 13, 2019

我經常想要獲取與使用者 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

不幸的是,getentPOSIX 似乎沒有指定該實用程序。

使用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
}

因為 POSIXid不支持 UID 參數,所以 eliffor 子句id不僅要測試是否id在 PATH 中,還要測試它是否會無錯誤地執行。這意味著它可能會執行id兩次,幸運的是這不會對性能產生明顯影響。也有可能同時執行idawk,但對性能的影響同樣可以忽略不計。

順便說一句,使用這種方法,無需儲存輸出。只有其中一個會執行,因此只有一個會列印輸出以供函式返回。

引用自:https://unix.stackexchange.com/questions/541417