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


23

필자는 종종 사용자 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 호환) 방법이 있습니까?


10
재미를 더하기 위해 여러 사용자 이름이 동일한 ID로 매핑 될 수 있다고 생각하십시오.
Stephen Kitt

이 질문의 맥락에서 동일한 ID에 매핑 여러 사용자 이름과 문제는 어느 쪽도 아니는 것입니다 getentid첫 경기 과거 아무것도 반환하지 않습니다; 모든 것을 찾는 유일한 방법은 사용자 데이터베이스가 허용하는 경우 모든 사용자를 열거하는 것입니다. ( /etc/passwd명백하게 정의 된 사용자를위한 작품을 찾는 것 .)
Stephen Kitt

1
감사합니다 @StephenKitt 내에서 이러한 항목을 생성 /etc/passwd하고 /etc/shadow이 시나리오를 테스트하려면 두 것을 확인 id하고 getent passwd당신이 설명하는대로 작동합니다. 어떤 단계에서 사용자가 여러 이름을 가진 시스템을 사용하게되면 이러한 시스템 유틸리티와 동일하게 수행하고 첫 번째 항목을 해당 사용자의 표준 이름으로 취급합니다.
Anthony G-

1
사용자 이름과 관련된해야합니까 POSIX는 사용자 ID를 필요로 전혀 ? 루트 전화 수있는 모든 프로그램 실행 setuid(some_id)및 요구 사항이 없습니다 some_id사용자 데이터베이스의 일부가 될 수있다. Linux의 사용자 네임 스페이스와 같은 것들로 인해 이는 스크립트에 대한 치명적인 가정이 될 수 있습니다.
mosvy

1
@Philippos는 UID를 로그인 이름으로 변환 getpwuid()하는 ls데 사용 되는 함수를 호출하는 비싼 방법 인 것 같습니다 . Gilles의 답변 은이를 달성하기위한보다 직접적이고 효율적인 방법입니다.
Anthony G-

답변:


14

이 작업을 수행하는 일반적인 방법 중 하나는 원하는 프로그램이 존재하며에서 사용할 수 있는지 테스트하는 것 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,이 방법을 사용하면 출력을 저장할 필요가 없습니다. 그중 하나만 실행되므로 함수를 반환하기 위해 출력 만 인쇄합니다.


같은 UID를 가진 여러 사용자 이름의 가능성에 대처하기 위해, 모든 것을 포장 iffi에서 { ... } | head -n 1. 즉, 첫 번째 UID 일치를 제외한 모든 항목을 삭제하십시오. 그러나 그것은 당신이 실행 한 프로그램의 종료 코드를 캡처해야 함을 의미합니다.
cas

답변 해주셔서 감사합니다. 나는 오지 않은 다른 유틸리티가 있기를 바랐지만 이것이 도움이되었습니다. idID를 피연산자로 받아들이지 않는 구현에 액세스 할 수 없으므로 종료 상태를 테스트하는 것이 문제가 될 수 있다고 생각했습니다. 존재하지 않는 로그인 이름이나 UID의 차이점을 구별하는 방법 존재하지 않습니다. 로그인 이름은 숫자로만 구성 할 수 있습니다. gnu.org/software/coreutils/manual/html_node/…
Anthony G-Monica의 정의

1
편집 할 때마다 기능이 더욱 강력 해집니다. :) 참고로, 아마도 이러한 유틸리티에는 다른 경로가 있다는 오프 오프 if command -v getent >/dev/null;대신에 사용하는 것이 좋습니다 if [ -x /usr/bin/getent ] ;.
Anthony G-

3
예. 나는 정기적 command -v으로이 목적 으로 사용 합니다 : pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html ( dash쉘 내장 으로 테스트 한 적이 있지만 ).
Anthony G-

1
@AnthonyGeoghegan 고대 시스템에서 작업해야한다면, type foo >/dev/null 2>/dev/null내가 본 모든 sh 에서 작동합니다. command비교적 현대적입니다.
Gilles 'SO- 악마 그만'

6

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

2
구문 분석 /etc/passwd은 전혀 휴대 할 수 없으며 LDAP와 같은 비 암호 파일 백엔드에서는 작동하지 않습니다.
R ..

나는 그것을 좋아합니다, 나는 그것을 훔칠 것입니다
cas

1
@R .. asker는 알고 있습니다.이 답변은 그렇지 않다고 주장하므로 의견의 요점은 무엇입니까?
Gilles 'SO- 악마 그만'

이 답변에 감사드립니다. 내가 알지 못하는 다른 유틸리티가 없다는 것을 확신합니다. POSIX는 UID를 로그인 이름으로 변환하기 위해 표준 C 함수를 지정하지만 반드시 해당 명령 (이외의 id)은 아닙니다 .
Anthony G-

2
마지막으로 시스템에 AC 컴파일러가 있는지 확인한 다음 제공된 getpwuid () 래퍼를 컴파일하십시오.
rackandboneman

5

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

3

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

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

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


1
댓글 제거 의견이 민사 여야한다고 참가자 모두 에게 상기시키고 싶습니다 .
terdon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.