对于我在学校的IT,我必须创建一个程序,输出用户ID和用户组ID就像命令" id",只是没有组。
这对我来说很好,但现在我必须使用方法getpwnam
来制作它的扩展版本:
它应该用作./id_extended USERNAME
,然后它应该输出该用户的uid和gid。我看了一下getpwnam(3),因为课程告诉我要去看那里。现在有一个使用getpwnam_r
和缓冲区等的例子,但这不是我们应该如何做到的;我们必须使用简单的getpwnam
。
我知道getpwnam
会返回一个指针,但该指针如何用于用户组ID和用户ID?
答案 0 :(得分:0)
OP应该在问题中包含他们的代码。让我们逐个检查它:
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
因为这是POSIX.1 C代码,所以代码应该从例如#define _POSIX_C_SOURCE 200809L
告诉我们需要POSIX.1-2008功能的C库头文件。
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()
以我自己的格式打印错误消息(而不是使用来自<stdio.h>
的例如perror()
,或%m
GNU扩展名。
下一部分,
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;
。< / p>
如果提供两个或多个参数,程序不会执行任何操作。这是非常令人惊讶的,并不可取。如果参数太多而打印出错误消息的if (argc == 1) { /* no arguments */ } else if (argc == 2) { /* one argument */ } else { /* two or more arguments */ }
将是一个更明智的选择。
在一个参数案例中(argc == 2
- 请记住argc
包含argv[0]
,这是用于执行此程序的名称),有两个不同的问题:第一,{ {1}}不是指针(可以通过声明它来修复,如上所示)。其次,更重要的一点是,没有检查pass
是否成功。
如果出现错误,man 3 getpwnam
告诉我们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. */
相当于POSIX.1 C中的if (!pass)
。
(使用较短的表单只是我个人的偏好。因为if (pass == NULL)
是非运算符,所以我实际上将第一个读作&#34;如果没有{ {1}}&#34; ;而我读第二个为&#34;如果!
为NULL&#34; 。前者更容易/更快/对我来说更舒服。)
另请注意,pass
或pass
没有printf格式说明符。虽然大多数代码使用uid_t
(gid_t
)并假设编译器将正确处理促销,但我个人更喜欢使用int
(%d
)并将结果转换为{{ 1}}明确;例如,
long
(大多数情况下,您会看到演员表写为%ld
,但除非您记得C中的运算符优先级规则,否则表达式 表达式的部分可能并不明显long
。为了让我们明确地告诉我们人类阅读代码 - 编译器肯定不关心! - ,我偶尔会把它写成 printf("uid=%ld (%s)\n", (long)(pass->pw_uid), pass->pw_name);
,因为演员项目显然是{{ 1}},即(long)pass->pw_uid
指向的结构中long
字段的值。)
最后,我想提出的程序逻辑有两处变化。
如果指定了用户名,则不使用(long)(pass->pw_uid)
,而是在未指定用户名时也使用它,以使用getpwuid(getuid())
获取当前用户信息。然后,在(pass->pw_uid)
指向的结构中打印信息的代码可能会停留在pw_uid
子句的主体之外。这样,只有一个pass
来打印信息,并且它始终是相同的形式,无论是为当前用户还是为名称指定的其他用户打印信息。
(struct passwd *pass
和pass
在POSIX.1中总是成功的;它们不会失败。这就是为什么在没有任何错误检查的情况下使用它们是完全可以的。)
第二个变化更像是一个补充。您可以使用getgrgid(pass->pw_uid)
或if (argc == ..)
获取指向printf()
的指针,该指针提供有关用户组的类似信息;尤其是getuid()
,这是该组的名称。
要使用getgid()
,您还应该在文件开头附近getgrgid(getgid())
。 (它还需要struct group
,但您已经应该->gr_name
和getgrgid()
需要它。)
上述信息应足以根据规定的要求编写完整的工作程序。但是,我不会列出完整的修复/修改程序,因为您将了解更多信息并获得自己完成自己工作的满意度 - 我们都避免通过提交工作来帮助贪婪者利用我们的工作作为他们自己。