사용자 ID와 연관된 사용자 이름을 얻는 POSIX 호환 방법

사용자 ID와 연관된 사용자 이름을 얻는 POSIX 호환 방법

나는 종종 사용자 ID와 연관된 로그인 정보를 얻고 싶은데, 이것이 일반적인 사용 사례임이 입증되었기 때문에 이를 수행하기 위한 쉘 함수를 작성하기로 결정했습니다. 나는 주로 GNU/Linux 배포판을 사용하지만 가능한 한 이식 가능한 스크립트를 작성하려고 노력하고 내가 하고 있는 작업이 POSIX와 호환되는지 확인합니다.

분석하다/etc/passwd

내가 시도한 첫 번째 방법은 구문 분석 /etc/passwd(사용 awk)이었습니다.

awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd

그러나 이 접근 방식의 문제점은 로그인이 로컬이 아닐 수 있다는 것입니다. 예를 들어 사용자 인증은 NIS 또는 LDAP를 통해 이루어질 수 있습니다.

getent명령 사용

를 사용하면 로컬이 아닌 NIS 또는 LDAP 데이터베이스도 쿼리하므로 getent passwd구문 분석보다 이식성이 더 좋습니다 ./etc/passwd

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가 유효하지 않거나 존재하지 않는 경우 0이 아닌 상태로 종료됩니다. 각 명령 호출의 종료 상태를 구문 분석하고 저장하는 더 길고 투박한 쉘 스크립트를 작성하기 전에 여기에 물어보고 싶다고 생각했습니다.

사용자 ID와 연결된 로그인을 가져오는 보다 우아하고 이식 가능한(POSIX 호환) 방법이 있습니까?

답변1

일반적인 접근 방식은 원하는 프로그램이 존재하는지, 에서 검색할 수 있는지 테스트하는 것입니다 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
}

idPOSIX는 UID 매개변수를 지원하지 않기 때문에 eliffor 절은 해당 매개변수가 PATH에 있는지 id테스트할 뿐만 아니라 제대로 작동할 수 있는지도 테스트합니다. id이는 두 번 실행될 수 있음을 의미합니다 id. 다행히 성능에 눈에 띄는 영향은 없습니다. id성능에 미치는 영향을 똑같이 무시하면서 두 가지를 동시에 실행할 수도 있습니다 .awk

그런데 이 접근 방식을 사용하면 출력을 저장할 필요가 없습니다. 그 중 하나만 실행되므로 그 중 하나만 함수에서 반환된 출력을 인쇄합니다.

답변2

POSIX 지정getpwuid사용자 데이터베이스에서 사용자 ID를 검색하여 해당 ID를 로그인 이름으로 변환하는 표준 C 함수입니다. GNU 소스 코드를 다운로드했습니다.핵심 도구id및 와 같은 유틸리티 구현에 사용되는 함수를 볼 수 있습니다 ls.

학습 연습으로 나는 단순히 함수 주위의 래퍼 역할을 하기 위해 이 빠르고 더러운 C 프로그램을 작성했습니다. 나는 대학 이후(수년 전) C로 프로그래밍한 적이 없으며 프로덕션에서 사용할 계획도 없지만 개념 증명으로 여기에 게시할 것이라고 생각했습니다(누군가 원하는 경우). 편집), 선택사항):

#include <stdio.h>
#include <stdlib.h>  /* atoi */
#include <pwd.h>

int main( int argc, char *argv[] ) {
    uid_t uid;
    if ( argc >= 2 ) {
        /* NB: atoi returns 0 (super-user ID) if argument is not a number) */
        uid = atoi(argv[1]);
    }
    /* Ignore any other arguments after the first one. */
    else {
        fprintf(stderr, "One numeric argument must be supplied.\n");
        return 1;
    }

    struct passwd *pwd;
    pwd = getpwuid(uid);
    if (pwd) {
        printf("The login name for %d is: %s\n", uid, pwd->pw_name);
        return 0;
    }
    else {
        fprintf(stderr, "Invalid user ID: %d\n", uid);
        return 1;
    }
}

NIS/LDAP로 테스트할 기회는 없었지만 동일한 사용자에 대한 항목이 여러 개 있으면 /etc/passwd첫 번째 항목을 제외한 모든 항목을 무시한다는 것을 알았습니다.

사용 예:

$ ./get_user ""
The login name for 0 is: root

$ ./get_user 99
Invalid user ID: 99

답변3

id.try id-and-return 구문 분석을 제외한 POSIX는 /etc/passwd실제로 이식 가능합니다.

BusyBox는 id사용자 ID를 허용하지 않지만 BusyBox가 있는 시스템은 일반적으로 구문 분석만으로 /etc/passwd충분한 자율 임베디드 시스템입니다.

id사용자 ID를 허용하지 않는 비 GNU 시스템을 발견하면 다음을 호출해 볼 수도 있습니다.getpwuidPerl을 통해(가능한 경우):

username=$(perl -e 'print((getpwuid($ARGV[0]))[0])) 2>/dev/null
if [ -n "$username" ]; then echo "$username"; return; fi

또는 파이썬:

if python -c 'import pwd, sys; print(pwd.getpwuid(int(sys.argv[1]))).pw_name' 2>/dev/null; then return; fi

답변4

일반적으로 말해서 이 작업을 수행하지 않는 것이 좋습니다. 사용자 이름에서 uid로의 매핑은 일대일이 아니며 변환할 수 있는 인코딩은 다음을 가정합니다.에서 돌아오다사용자 이름의 uid를 얻으면 모든 것이 중단됩니다. 예를 들어 컨테이너의 및 파일이 모든 사용자 이름과 그룹 이름을 ID 0에 매핑하도록 하여 passwd완전히 루트 없는 사용자 네임스페이스 컨테이너를 실행하는 경우가 많습니다 group. 이렇게 하면 설치 프로그램이 chown실패 없이 작동할 수 있습니다. 그러나 무언가가 0을 uid로 다시 변환하려고 시도하고 예상한 결과를 얻지 못하면 뚜렷한 이유 없이 충돌이 발생합니다. 따라서 이 예에서는 사용자 이름을 다시 변환하고 비교하는 대신 uid로 변환하고 해당 공간에서 비교해야 합니다.

정말로 이 작업을 수행해야 하는 경우, 루트 사용자라면 임시 파일을 생성하고 chown이를 uid에 쓴 다음 ls다시 읽기를 사용하고 소유자 이름을 구문 분석하여 반이식 가능하게 만들 수 있습니다. 하지만 나는 이미 찾은 방법 중 하나처럼 표준화되지 않았지만 "실제로 이식 가능한" 잘 알려진 방법을 사용하겠습니다.

하지만 다시 말하지만 이러지 마세요. 때때로 하기 어려운 일은 당신에게 메시지를 보내는 것입니다.

관련 정보