对于我在学校的IT,我必须编写一个程序来输出用户ID和用户组ID,就像命令“ id”一样,而没有组。
这对我来说很好,但是现在我必须使用方法getpwnam
对其进行扩展:
它应用作./id_extended USERNAME
,然后应输出该用户的uid和gid。我看了getpwnam(3)的观点,因为这课告诉我要去那里做。现在有一个将getpwnam_r
与缓冲区等配合使用的示例,但这不是我们应该采用的方式;我们必须使用简单的getpwnam
。
我知道getpwnam
返回一个指针,但是该指针如何用于用户组ID和用户ID?
最佳答案
OP应该在问题中包含其代码。让我们对其进行部分检查:
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
由于这是POSIX.1 C代码,因此代码应以例如
#define _POSIX_C_SOURCE 200809L
告诉C库头文件我们需要POSIX.1-2008功能。getpwnam()
需要#include <pwd.h>
,但还需要#include <sys/types.h>
(未列出)。如果使用了
getuid()
和getgid()
,则需要#include <unistd.h>
(已列出)。标准I / O需要
#include <stdio.h>
,而#include <errno.h>
提供errno
和错误代码。我个人也将#include <string.h>
用作strerror()
,因为我喜欢使用fprintf()
以自己的格式打印错误消息(而不是使用例如perror()
或<stdio.h>
GNU扩展名的%m
。下一部分,
struct passwd *getpwnam(const char *name);
uid_t getuid(void);
uid_t geteuid(void);
struct passwd pass;
有很多问题:首先,您不需要声明C库函数;头文件已经为我们声明了它们。其次,
getpwnam()
的返回值是指向struct passwd
的指针-这就是struct passwd *
的含义! -,因此声明struct passwd pass
为零。第三,如果仅在pass
中使用main()
,则应在main()
中将其声明为局部变量,而不是全局变量(在任何函数之外)。在最后一部分,
int main(int argc,char* argv[]) {
if(argc == 1){
printf("user: %d\ngroup: %d\n",getuid(),getgid());
}
if(argc == 2){
pass = getpwnam(argv[1]);
printf("%s %ld",pass.pw_uid,pass.pw_gid );
}
}
我们看到正确定义了
main()
(采用int argc
和char *argv[]
);但是,没有声明要求的return (some int)
语句。最好通过在末尾添加return EXIT_SUCCESS;
来解决。由于
pass
仅在main()
中使用,因此它应该是局部变量,因此main()
的主体应以其正确的声明开始:struct passwd *pass;
。如果提供两个或多个参数,则该程序不执行任何操作。这是非常令人惊讶的,也是不希望的。在参数过多的情况下显示错误消息的
if (argc == 1) { /* no arguments */ } else if (argc == 2) { /* one argument */ } else { /* two or more arguments */ }
在这里是更明智的选择。在一个参数的情况下(
argc == 2
-请记住,argc
包括argv[0]
,这是用于执行该程序的名称),存在两个独立的问题:首先,pass
不是指针(可以如上所示通过声明对其进行修复)。其次,更重要的一点是,没有检查getpwnam()
是成功还是失败。如果发生错误,
man 3 getpwnam
告诉我们,如果发生错误,getpwnam()
将返回NULL
,其中errno
描述该错误。这意味着如果正确声明了pass
,则应使用例如 pass = getpwnam(argv[1]);
if (!pass) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
/* pass->pw_uid has user ID,
pass->pw_gid has group ID,
pass->pw_name has user name,
and so on. */
请注意,
if (!pass)
等效于POSIX.1 C中的if (pass == NULL)
。(使用较短的格式只是我个人的喜好。因为
!
是not运算符,所以我实际上将第一个读为“如果没有pass
”;而我将第二个读为“如果pass
为NULL” 。前者对我来说更轻松/更快/更舒适。)另请注意,没有
uid_t
或gid_t
的printf格式说明符。虽然大多数代码使用int
(%d
)并假定编译器将正确处理升级,但我个人更喜欢使用long
(%ld
)并将结果显式转换为long
;例如, printf("uid=%ld (%s)\n", (long)(pass->pw_uid), pass->pw_name);
(大多数情况下,您会看到强制转换写为
(long)pass->pw_uid
,但是除非您还记得C语言中的运算符优先级规则,否则可能不清楚将表达式的哪一部分强制转换为long
。阅读代码-编译器绝对不在乎--我偶尔会写为(long)(pass->pw_uid)
,因为强制转换项显然是(pass->pw_uid)
,即结构中pw_uid
字段的值指向通过pass
。)最后,我想建议一下程序逻辑中的两个更改。
如果没有指定用户名,则不使用
struct passwd *pass
,而是在未指定用户名的情况下也使用它,以使用getpwuid(getuid())
获取当前用户信息。然后,以pass
所指向的结构打印信息的代码可能会停留在if (argc == ..)
子句的主体之外。这样,只有一个printf()
可以打印信息,并且无论信息是为当前用户打印还是由名称指定的其他用户打印,其格式始终相同。(
getuid()
和getgid()
在POSIX.1中总是成功的;它们不会失败。这就是为什么可以使用它们而不进行任何错误检查的原因。)第二个更改更像是附加项。您可以使用
getgrgid(pass->pw_uid)
或getgrgid(getgid())
来获取指向struct group
的指针,该指针在用户组上提供相似的信息。特别是->gr_name
,这是组的名称。要使用
getgrgid()
,还应该在文件开头附近使用#include <grp.h>
。 (它也需要#include <sys/types.h>
,但是您已经应该拥有它,因为getpwnam()
和getpwuid()
都需要它。)以上信息应足以根据规定的要求编写一个可以正常运行的程序。但是,我不会列出完整的固定/修改程序,因为您会学到更多,并且会满意地自己完成自己的工作-而且我们都避免通过将我们的工作作为自己的工作来帮助自由登载者利用我们的努力拥有。
关于c - C方法ID和getpwnam(3),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43768015/