나는 종종 사용자 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
불행하게도 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가 유효하지 않거나 존재하지 않는 경우 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
}
id
POSIX는 UID 매개변수를 지원하지 않기 때문에 elif
for 절은 해당 매개변수가 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 시스템을 발견하면 다음을 호출해 볼 수도 있습니다.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
답변4
일반적으로 말해서 이 작업을 수행하지 않는 것이 좋습니다. 사용자 이름에서 uid로의 매핑은 일대일이 아니며 변환할 수 있는 인코딩은 다음을 가정합니다.에서 돌아오다사용자 이름의 uid를 얻으면 모든 것이 중단됩니다. 예를 들어 컨테이너의 및 파일이 모든 사용자 이름과 그룹 이름을 ID 0에 매핑하도록 하여 passwd
완전히 루트 없는 사용자 네임스페이스 컨테이너를 실행하는 경우가 많습니다 group
. 이렇게 하면 설치 프로그램이 chown
실패 없이 작동할 수 있습니다. 그러나 무언가가 0을 uid로 다시 변환하려고 시도하고 예상한 결과를 얻지 못하면 뚜렷한 이유 없이 충돌이 발생합니다. 따라서 이 예에서는 사용자 이름을 다시 변환하고 비교하는 대신 uid로 변환하고 해당 공간에서 비교해야 합니다.
정말로 이 작업을 수행해야 하는 경우, 루트 사용자라면 임시 파일을 생성하고 chown
이를 uid에 쓴 다음 ls
다시 읽기를 사용하고 소유자 이름을 구문 분석하여 반이식 가능하게 만들 수 있습니다. 하지만 나는 이미 찾은 방법 중 하나처럼 표준화되지 않았지만 "실제로 이식 가능한" 잘 알려진 방법을 사용하겠습니다.
하지만 다시 말하지만 이러지 마세요. 때때로 하기 어려운 일은 당신에게 메시지를 보내는 것입니다.