사용자 ID와 연관된 사용자 이름을 가져 오는 POSIX 호환 방법 었습니다 . awk -v uid=”$uid”

필자는 종종 사용자 ID와 관련된 로그인 이름을 얻고 싶습니다. 일반적인 사용 사례로 입증 되었기 때문에이를 위해 쉘 함수를 작성하기로 결정했습니다. 나는 주로 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 사용자 ID에 대한 데이터를 가져 오기위한 POSIX 표준 유틸리티입니다.

BSD 및 GNU 구현에서는 사용자 ID를 피연산자로 사용합니다.

이는 사용자 ID와 연관된 로그인 이름을 인쇄하는 데 사용될 수 있음을 의미합니다.

id -nu "$uid"

그러나 피연산자로 사용자 ID를 제공하는 것은 POSIX에 지정되어 있지 않습니다. 피연산자로 로그인 이름 을 사용하는 것만 설명합니다 .

위의 모든 것을 결합

위의 세 가지 접근 방식을 다음과 같은 방식으로 결합하는 것을 고려했습니다.

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 호환) 방법이 있습니까?



답변

이 작업을 수행하는 일반적인 방법 중 하나는 원하는 프로그램이 존재하며에서 사용할 수 있는지 테스트하는 것 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절 은 PATH에 있는지 여부뿐만 아니라 오류없이 실행 될지 id여부를 테스트해야합니다 id. 이는 id두 번 실행될 수 있으며 , 결과적 으로 성능에 눈에 띄는 영향을 미치지 않습니다. 모두 가능성도 id하고 awk같은 무시할 성능 저하로 실행됩니다.

BTW,이 방법을 사용하면 출력을 저장할 필요가 없습니다. 그중 하나만 실행되므로 함수를 반환하기 위해 출력 만 인쇄합니다.


답변

POSIX에는 이외의 도움이되는 것은 없습니다 id. id파싱을 시도 하고 다시 파싱하는 /etc/passwd것은 아마도 실제로 휴대하는 것만 큼 이식성이 좋을 것입니다.

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

id사용자 ID를 받아들이지 않는 비 GNU 시스템이있는 경우 getpwuid, 사용 가능한 경우 Perl을 통해 전화 를 걸 수도 있습니다.

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

답변

POSIX getpwuid는 사용자 데이터베이스에서 ID를 로그인 이름으로 변환 할 수있는 사용자 ID를 검색하는 표준 C 함수로 지정합니다. GNU coreutils 용 소스 코드를 다운로드 했으며이 기능이 id및 등의 유틸리티 구현에 사용되는 것을 볼 수 있습니다 ls.

학습 연습으로, 나는이 기능에 대한 래퍼 (wrapper) 역할을하기 위해이 더럽고 더러운 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

답변

일반적으로, 나는 이것을하지 않는 것이 좋습니다. 사용자 이름에서 uid 로의 매핑은 일대일이 아니며 , 사용자 이름을 얻기 위해 uid 에서 다시 변환 할 수있는 인코딩 가정은 문제가 발생 합니다. 예를 들어, 컨테이너의 passwdgroup파일이 모든 사용자 및 그룹 이름을 id 0에 매핑하도록 하여 루트가없는 사용자 네임 스페이스 컨테이너를 자주 실행 합니다. 이를 통해 패키지 설치가 chown실패 없이 작동 합니다. 그러나 어떤 것이 0을 uid로 다시 변환하려고 시도하고 기대 한 것을 얻지 못하면, 그것은 무너질 것입니다. 따라서이 예에서는 사용자 이름을 다시 변환하고 비교하는 대신 uid로 변환하고 해당 공간을 비교해야합니다.

이 작업을 실제로 수행 해야하는 경우 루트 파일 인 경우 임시 파일을 만들고 chownuid에 임시 파일을 만든 다음 ls소유자 이름을 읽고 구문 분석하는 데 사용하여 반 이식 가능하게 수행 할 수 있습니다 . 그러나 이미 표준화 된 것이 아니라 “실제로 이식 할 수있는”잘 알려진 접근법을 사용하려고합니다.

그러나 다시이 작업을 수행하지 마십시오. 때로는 어려운 일이 당신에게 메시지를 보내는 것입니다.