基本答案是使用(否定的)“扫描集”——阅读sscanf() 的手册。
if (sscanf(buf, "%[^:]:%*[^:]:%*[^:]:%u", szName, &ncUser_gid) != 2)
这会将一系列非冒号读入szName,然后跳过冒号、密码字段、冒号、UID 字段、冒号,然后将下一个字段中的数字读入ncUser_gid。它还会检查您是否获得了这两个值,同时忽略其他尾随字段(comment、home、shell)。
请注意,由于您使用的是sscanf(),因此实际上不需要处理任何尾随字段。此外,密码文件条目中有 7 个字段,而不是 6 个字段。使用sscanf(),这不是问题。如果您正在从文件中读取,那将是。此外,如果您正在从文件中读取,您将不得不担心扫描集不会跳过前导空格,这将是前一行输入留下的换行符。对于文件流解析,您需要使用:
int rc;
if ((rc = fscanf(fp, " %[^:]:%*[^:]:%u:%u:%[^:]:%[^:]:%[^:]",
username, &uid, &gid, comment, homedir, shell)) != 5 && rc != 6)
…handle format error…
if (rc == 5)
shell[0] = '\0';
请注意,shell 字段不必有任何数据。这也会违反空的评论字段,但通常会填充。请注意,它跳过了密码;它在现代版本的 Unix 中很少有兴趣。
sscanf()例子
#include <stdio.h>
int main(void)
{
char buf[] = "root:*:0:1:System Administrator:/var/root:/bin/sh";
char szName[10] = "Pygmalion"; // Make sure it isn't empty!;
unsigned int ncUser_gid = 23456; // Make sure it isn't zero!
if (sscanf(buf, "%[^:]:%*[^:]:%*[^:]:%u", szName, &ncUser_gid) != 2)
printf("Ooops!\n");
else
printf("User: [%s]; GID = %u\n", szName, ncUser_gid);
return 0;
}
输出:
User: [root]; GID = 1
(我破解了条目,所以 UID 和 GID 不同。)
fscanf()例子
#include <stdio.h>
int main(void)
{
const char passwd[] = "/etc/passwd";
FILE *fp = fopen(passwd, "r");
if (fp == 0)
{
fprintf(stderr, "failed to open '%s' for reading\n", passwd);
return 1;
}
char username[64];
unsigned uid;
unsigned gid;
char comment[64];
char homedir[64];
char shell[64];
int rc;
while (!feof(fp))
{
if ((rc = fscanf(fp, " %63[^:\n]:%*[^:\n]:%u:%u:%63[^:\n]:%63[^:\n]:%63[^:\n]",
username, &uid, &gid, comment, homedir, shell)) != 5 && rc != 6)
{
int c;
while ((c = getc(fp)) != EOF && c != '\n')
;
}
else
{
if (rc == 5)
shell[0] = '\0';
printf("[%s] %u %u [%s] [%s] [%s]\n", username, uid, gid, comment, homedir, shell);
}
}
return 0;
}
请注意,在 Mac 上,密码文件以多行 # cmets 开头。 %[^:\n] 符号或类似符号是必需的,以避免解析文件的该部分时出现问题。在文件中没有此类注释行的健全系统上,您可能没有它们就可以逃脱。还要注意,代码会保护自己免受字符串字段的溢出。
此外,对于 UID 和 GID,我继续使用 unsigned 整数,但对于 UID 和 GID,nobody 具有负值 -2,因此带符号的类型可能会更好。
示例输出:
[nobody] 4294967294 4294967294 [Unprivileged User] [/var/empty] [/usr/bin/false]
[root] 0 0 [System Administrator] [/var/root] [/bin/sh]
[daemon] 1 1 [System Services] [/var/root] [/usr/bin/false]
…
所有用户都在我的 Mac 上指定了特定的 shell,因此“rc == 5”代码尚未真正经过测试。
样本输出:
[# Open Directory.
##
nobody] 4294967294 4294967294 [Unprivileged User] [/var/empty] [/usr/bin/false
JFTR:在运行 macOS 10.12.5 且使用 GCC 7.1.0 的 Mac 上进行了测试。
编译命令行如:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes pw89.c -o pw89
$