我正在撰寫一個從 /etc/passwd 讀取并輸出用戶名和 shell 的程式。
例如,這里是 /etc/passwd 檔案的第一行:
root:x:0:0:root:/root:/bin/bash
我只需要輸出用戶和外殼。在這種情況下,它將列印:
root:/bin/bash
這些值是分開的,':'所以我只需要列印第一個之前的':'字串和第六個之后的字串':'
這是我到目前為止的代碼:
#include <string.h>
#define BUFFERSIZE 4096
int printf(const char *text, ...);
int main(void) {
int fd;
int buff_size = 1;
char buff[BUFFERSIZE];
int size;
fd = open("/etc/passwd", O_RDONLY);
if (fd < 0) {
printf("Error opening file \n");
return -1;
}
size = strlen(buff - 17);
size = size 1;
while ((size = read(fd, buff, 1)) > 0) {
buff[1] = '\0';
write(STDOUT_FILENO, buff, size);
}
}
(我正在創建原型,printf因為其中一項要求是撰寫不包含<stdio.h>or的程式<stdlib.h>)
uj5u.com熱心網友回復:
當您評估時,您的程式具有未定義的行為strlen(buff - 17)。目前尚不清楚您為什么要這樣做。
您可以通過以下簡單步驟解決問題:
- 一次讀取一個位元組
- 數一下
':'就行 - 如果計數等于
0或等于 ,則輸出位元組6。 - 在換行處重置計數(并列印換行符)
請注意,read(fd, &b, 1)如果出現錯誤或中斷,則write(1, &b, 1)回傳,如果是-1,則應重新啟動。errnoEINTR
這是修改后的版本:
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
int fd;
unsigned char b;
int count;
ssize_t ret;
fd = open("/etc/passwd", O_RDONLY);
if (fd < 0) {
write(2, "Error opening /etc/password\n", 28);
return 1;
}
count = 0;
for (;;) {
ret = read(fd, &b, 1);
if (ret == 0) { // end of file
break;
}
if (ret < 0) { // error
if (errno == EINTR)
continue;
write(2, "Read error on /etc/password\n", 28);
return 1;
}
if (b == '\n') {
// reset count, print b
count = 0;
} else
if (b == ':') {
// increment count, print ':' only if count == 1
count = count 1;
if (count != 1)
continue;
} else
if (count != 0 && count != 6) {
// print b only if count is 0 or 6
continue;
}
for (;;) {
ret = write(1, &b, 1);
if (ret == 1)
break;
if (ret < 0 && errno = EINTR)
continue;
write(2, "Write error\n", 12);
return 1;
}
}
close(fd);
return 0;
}
uj5u.com熱心網友回復:
另一種方法是使用單個回圈和一個狀態變數來根據讀取的冒號數跟蹤您在每一行中的狀態。狀態變數ncolon在下面執行此操作。本質上,您讀取每個字符并檢查回圈是否處于應將字符寫入輸出的狀態。無論您是在第一個之前還是在最后一個之后,您都可以根據冒號的數量進行寫入。
總而言之,你可以這樣做:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main (int argc, char **argv) {
int fd, /* file descriptor */
ofd = STDOUT_FILENO, /* output file descriptor */
ncolon = 0; /* counter - number of colons seen */
/* open file given on command line or read from stdin otherwise */
if ((fd = argc > 1 ? open (argv[1], O_RDONLY) : STDIN_FILENO) == -1) {
return 1;
}
for (;;) { /* loop continually */
unsigned char c; /* storage for character */
int rtn; /* var to save return */
if ((rtn = read (fd, &c, 1)) < 1) { /* validate read of 1 char */
if (rtn == -1) { /* return on error */
return 1;
}
break; /* break read loop on EOF */
}
if (ncolon < 1 || ncolon == 6) { /* if before 1st or after last */
write (ofd, &c, 1); /* output char */
}
if (c == '\n') { /* reset ncolon on newline */
ncolon = 0;
}
else if (c == ':') { /* increment on colon */
ncolon = 1;
}
}
if (fd != STDIN_FILENO) { /* close file */
close (fd);
}
}
示例使用/輸出
$ ./read_etc-passwd /etc/passwd
root:/bin/bash
messagebus:/usr/bin/false
systemd-network:/usr/sbin/nologin
systemd-timesync:/usr/sbin/nologin
nobody:/bin/bash
mail:/usr/sbin/nologin
chrony:/usr/sbin/nologin
...
確認格式
$ diff <(./read_etc-passwd /etc/passwd) <(awk -F: '{print $1":"$7}' /etc/passwd)
(沒有輸出意味著程式輸出和awk輸出相同)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/520288.html
標籤:C
