目录
前言
正文
原文:
要求总结:
一`点一点来:
grep学习:
glob理解:
dirent 目录函数:
加载日志文件:
strstr与strcmp:
非首次尝试:
非二次 :
老师的:
读老师的函数一点一点来的补充:
后语
3、老师的程序读后感:
4、glob与dirent区别:
前言
嗨,项目初体验,收获满满!
认知的圆越大,感受的压力就越多!所以说要化圆为梭、为剑,方能驰骋知识的宇宙!
正文
原文:
看到一个箭头指向的按钮了吗?你也可以试一下哦,嘿嘿……只不过可能获得 满身全是浮躁的buff。尤其是当下炎炎夏日,还能增幅120%!
要求总结:
根据grep的使用及glob函数理解,把项目要求整理下
- logfind pattern file 这个命令日志文件夹下到在指定文件中搜索匹配指定模式的行,并将匹配的行输出到屏幕上。例如,logfind "eJSu" file.log将搜索file.txt文件中包含"eJSu"的行。
- logfind pattern 到日志文件夹查找匹配指定模式的文件,并将匹配的行输出到屏幕上,如果是多个词语就是“和”的关系。
- loafing -o pattern 到日志文件夹查找匹配指定模式的文件,并将匹配的行输出到屏幕上,如果是多个词语就是“或”的关系
感觉原文翻译的4描述很别扭,暂且这样,最后看看与作者的代码差距在哪里吧!
一`点一点来:
grep学习:
grep是一个非常强大的命令行工具,用于在文件中搜索指定的模式。它可以使用正则表达式作为模式,并可以通过多种选项和参数来进行精确的搜索和过滤。grep的使用方法和实例如下:
-
基本用法: grep pattern file 这个命令将在指定文件中搜索匹配指定模式的行,并将匹配的行输出到屏幕上。例如,grep "hello" file.txt将搜索file.txt文件中包含"hello"的行。
-
使用正则表达式: grep支持使用正则表达式作为模式。正则表达式可以包含特殊字符和元字符,用于指定搜索模式的规则。例如,grep "^[0-9]" file.txt将搜索file.txt文件中以数字开头的行。
-
忽略大小写: grep -i pattern file 使用-i选项可以忽略模式的大小写,即不区分大小写地搜索匹配项。例如,grep -i "hello" file.txt将搜索file.txt文件中包含"hello"或"HELLO"等的行。
-
显示行号: grep -n pattern file 使用-n选项可以显示匹配行的行号。行号将显示在每行的前面。例如,grep -n "hello" file.txt将搜索file.txt文件中包含"hello"的行,并显示行号。
-
搜索多个文件: grep pattern file1 file2 ... 可以同时在多个文件中搜索指定模式。grep将逐个文件输出匹配的行。例如,grep "hello" file1.txt file2.txt将搜索file1.txt和file2.txt文件中包含"hello"的行。
-
递归搜索目录: grep -r pattern directory 使用-r选项可以递归地搜索指定目录及其子目录中的文件。例如,grep -r "hello" /path/to/directory将搜索/path/to/directory目录及其子目录中包含"hello"的行。
-
打印匹配行之前或之后的内容: grep -A num -B num -C num pattern file 使用-A, -B, -C选项可以打印匹配行之前(-B)、之后(-A)或之前和之后(-C)的指定行数的内容。例如,grep -A 2 "hello" file.txt将搜索file.txt文件中包含"hello"的行,并打印匹配行及其后两行的内容。
-
反向匹配: grep -v pattern file 使用-v选项可以反向匹配,即只输出不匹配模式的行。例如,grep -v "hello" file.txt将搜索file.txt文件中不包含"hello"的行。
-
统计匹配行数: grep -c pattern file 使用-c选项可以统计匹配模式的行数,并将结果输出。例如,grep -c "hello" file.txt将统计file.txt文件中包含"hello"的行数。
glob理解:
在C语言中,可以使用glob
函数来实现文件名模式匹配,查找符合特定模式的文件名。
glob
函数的原型如下:
#include <glob.h>
int glob(const char *pattern, int flags, int (*errfunc)(const char *epath, int errno), glob_t *pglob);
参数解释:
pattern
:需要匹配的模式字符串flags
:匹配选项,可以是以下常量的按位或组合:GLOB_ERR
:发生错误时,停止匹配GLOB_MARK
:如果匹配到的是目录,则在末尾添加一个斜杠GLOB_NOSORT
:不对匹配到的文件名进行排序GLOB_NOESCAPE
:不对模式中的特殊字符进行转义GLOB_ONLYDIR
:只匹配目录GLOB_TILDE
:对~
进行波浪号扩展GLOB_TILDE_CHECK
:只对~
进行波浪号扩展,如果用户不存在,则报错
errfunc
:错误处理函数,可以为NULL
pglob
:用于存储匹配结果的glob
结构体变量:size_t gl_pathc
:表示匹配到的文件名数量。char **gl_pathv
:表示匹配到的文件名列表,是一个指向字符数组的指针。size_t gl_offs
:用于控制gl_pathv
数组的起始位置。
glob
函数的返回值为0表示匹配成功,其他值表示错误。
下面是一个使用glob
函数的示例:
#include <stdio.h>
#include <glob.h>
int main() {
glob_t pglob;
int i, ret;
ret = glob("/path/to/files/*.txt", 0, NULL, &pglob);
//调用glob函数进行模式匹配。指定的模式是"/path/to/files/*.txt",
//表示匹配以.txt结尾的文件名。传入的flags参数为0,表示使用默认的匹配选项。
if (ret != 0) {
printf("Error in glob\n");
return 1;
}
for (i = 0; i < pglob.gl_pathc; i++) {
printf("%s\n", pglob.gl_pathv[i]);
}
globfree(&pglob);
return 0;
}
在上述示例中,glob
函数被用来查找目录/path/to/files/
下所有以.txt
结尾的文件名,并将结果存储到pglob
结构体变量中。然后通过遍历pglob.gl_pathv
数组来输出匹配到的文件名。最后,使用globfree
函数释放pglob
变量占用的内存。
dirent 目录函数:
不知道何如开始这个项目时,就随便搜索了下,发现要用到目录函数。目录函数没接触过,不熟悉,那就先熟悉dirent。
#include <stdio.h>
#include <dirent.h>
#include "dbg.h"
int main(int argc, char *argv[])
{
DIR *dir = opendir(".");//这个点号 是表示当前文件夹
check(dir != NULL, "Failed open the dir");
struct dirent *ent ;
char filepath[1024];
int i = 0;
while ((ent = readdir(dir)) != NULL){//此循环是经典的用法,循环一次,
//读取一个文件,还能检查是否读完
snprintf(filepath, sizeof(filepath), "%s/%s","lcthw", ent->d_name);
printf("i>>:%d%s",i++, filepath);
}
closedir(dir);
return 0;
error:
return -1;
}
由于用的是linux,对它曾经一知半解过,所以我首先迷失在了万物起源"."(点号)。原来它就是代表当前文件夹,你把程序放在哪个文件夹,它就遍历哪个文件夹。
由于对文件函数曾经好好的较量过,发现这个目录函数使用过程与文件使用过程相似。可能是因为一个是文件流,一个目录流,都是流。嗯,一个FILE, 一个DIR。
看看是不是?
好的,dirent基本操作熟悉了,课文要求我们跑到指定地方搜索日志文件。
加载日志文件:
什么是系统日志文件及存放位置呢?
我用的是linux,系统日志文件通常被存储在/var/log
目录下。各个日志文件的具体路径和后缀名如下:
/var/log/messages
:系统和服务的一般消息日志。/var/log/auth.log
:包含系统授权和认证活动的日志。/var/log/kern.log
:内核日志,记录与内核相关的消息和错误。/var/log/syslog
:所有系统日志的综合文件,包括其他日志文件的内容。/var/log/dmesg
:内核环缓冲区的内容,用于存储系统启动时的消息。/var/log/boot.log
:在系统引导期间生成的启动日志。/var/log/secure
:记录与系统安全性相关的信息,如认证、授权和权限更改。
此外,不同的服务和应用程序可能会有自己的日志文件,它们通常位于/var/log
目录下,具有与服务或应用程序相关的名称和后缀名。
需要注意的是,这只是一些常见的日志文件示例,具体的日志文件路径和后缀名可能因Linux发行版和配置而异。在某些情况下,我们也可以在/etc/rsyslog.conf
文件中配置日志文件的位置和后缀名。
我们就找/var/log目录下的日志文件吧,后缀名log。看看我的乌班图linux日志文件:
健壮的代码需要深厚的内功的,显然我刚有气感。所以咱们就简单的以后缀名查找,示例如下。
#include <stdio.h>
#include <string.h>
int main() {
char filename[] = "example.log";
char *extension;
// 找到文件名中的最后一个点号
extension = strrchr(filename, '.');
if (extension != NULL) {
// 比较后缀名是否为.log
if (strcmp(extension, ".log") == 0) {
printf("The file is a log file.\n");
} else {
printf("The file is not a log file.\n");
}
} else {
printf("The file has no extension.\n");
}
return 0;
}
strstr与strcmp:
在C语言中,strstr
和strcmp
是字符串处理函数。
strstr
函数用于在一个字符串中查找指定子字符串的第一次出现,并返回子字符串的指针。其函数原型为:
char* strstr(const char* str1, const char* str2);
其中,str1
是要搜索的字符串,str2
是要查找的子字符串。如果找到了子字符串,则返回子字符串第一次出现的位置的指针;如果没有找到子字符串,则返回NULL
。
示例用法:
const char* str1 = "Hello, World!";
const char* str2 = "World";
const char* result = strstr(str1, str2);
if (result)
printf("Found at position: %d\n", result - str1);
else
printf("Not found.\n");
上述代码将输出 "Found at position: 7",因为子字符串 "World" 在字符串 "Hello, World!" 中的位置从索引7开始。
strcmp
函数用于比较两个字符串的大小。其函数原型为:
int strcmp(const char* str1, const char* str2);
其中,str1
和str2
都是要进行比较的字符串。函数返回值为0表示两个字符串相等,返回值小于0表示str1
小于str2
,返回值大于0表示str1
大于str2
。
示例用法:
const char* str1 = "Hello";
const char* str2 = "World";
int result = strcmp(str1, str2);
if (result == 0)
printf("The strings are equal.\n");
else if (result < 0)
printf("The first string is smaller.\n");
else
printf("The first string is larger.\n");
上述代码将输出 "The first string is smaller.",因为字符串 "Hello" 在字母表中比字符串 "World" 小。
非首次尝试:
#define NDEBUG
#include <stdio.h>
#include <string.h>
#include "dbg.h"
#include <stdlib.h>
#include <dirent.h>
#define N 256
void file_find(char *filename, char *searchword)
{
char str[N];
FILE *fp;
fp = fopen(filename, "r");
check(fp, "fail open");
debug("filename:%s", filename);
while(fgets(str, N, fp) != NULL){
if(strstr(str,searchword) != NULL){//用于在一个字符串中查找另一个字符串第一次出现的位置。没有则返回NULL。
printf("str:%s", str);
}
}
fclose(fp);
return;
error:
exit(-1);
}
int dir_find( char *searchword)
{
DIR *dir =opendir("/var/log");
check(dir, "fail read dir");
struct dirent *ent ;
char str[N];
FILE *fp;
while ((ent = readdir(dir)) != NULL){
snprintf(str, sizeof(str), "%s", ent->d_name);
debug("filename:%s", str);
fp = fopen(str, "r");
check(fp, "fail open the file in the dir");
while(fgets(str, N, fp) != NULL){
if(strstr(str,searchword) != NULL){//用于在一个字符串中查找另一个字符串第一次出现的位置。没有则返回NULL
printf("str:%s", str);
}
}
}
fclose(fp);
return 0;
error:
exit(-1);
}
int main(int argc, char *argv[])
{
check((argc >1), "Please input arg");
if(strcmp(argv[1], "-o") == 0){//如果两个字符串一样则返回1. "-o"biaoshi huo "||".
debug();
check(argc = 2,"USAGE: ./file -o patter");
for (int i = 2; i<argc; i++){
if(strstr(argv[2], ".log") != NULL){
debug("argv[2]:%s, argv[i]:%s", argv[2], argv[i]);
file_find(argv[2], argv[i++]);
}else{
dir_find(argv[i]);
}
}
}else{
int j =1;
if(strstr(argv[1], ".") != NULL){
while(j < (argc)){
file_find(argv[1], argv[j++]);
}else{
while (dir_find(argv[j]) == 0 ){
if (j < (argc)){
j++;
continue;
}else{
printf("find the file\n");
}
}
printf("No find\n");
}
}
log_info("right end");
return 0;
error:
return -1;
}
非二次 :
留个坑,嘿嘿……
老师的:
#define NDEBUG
#include "dbg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include <assert.h>
const size_t MAX_LINE = 1024;
int list_files(glob_t *pglob)
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(".logfind", "r");
int glob_flags = GLOB_TILDE;
int i = 0;
int rc = -1;
check(pglob != NULL, "Invalid glob_t given.");
check_mem(line);
check(file, "Failed to open .logfind. Make that first.");
while(fgets(line, MAX_LINE-1, file) != NULL) {
size_t line_length = strnlen(line, MAX_LINE - 1);
assert(line_length < MAX_LINE && "Got a line length too long.");
line[line_length] = '\0'; // drop the \n ending
debug("Globbing %s", line);
rc = glob(line, glob_flags, NULL, pglob);
check(rc == 0 || rc == GLOB_NOMATCH, "Failed to glob.");
// dumb work around to a stupid design in glob
if(glob_flags == GLOB_TILDE) glob_flags |= GLOB_APPEND;
}
for(i = 0; i < pglob->gl_pathc; i++) {
debug("Matched file: %s", pglob->gl_pathv[i]);
}
rc = 0; // all good
error: // fallthrough
if(line) free(line);
return rc;
}
int found_it(int use_or, int found_count, int search_len)
{
debug("use_or: %d, found_count: %d, search_len: %d", use_or, found_count, search_len);
if(use_or && found_count > 0) {
return 1;
} else if(!use_or && found_count == search_len) {
return 1;
} else {
return 0;
}
}
int scan_file(const char *filename, int use_or, int search_len, char *search_for[])
{
char *line = calloc(MAX_LINE, 1);
FILE *file = fopen(filename, "r");
int found_count = 0;
int i = 0;
check_mem(line);
check(file, "Failed to open file: %s", filename);
// read each line of the file and search that line for the contents
while(fgets(line, MAX_LINE-1, file) != NULL)
{
for(i = 0; i < search_len; i++) {
if(strcasestr(line, search_for[i]) != NULL) {
debug("file: %s, line: %s, search: %s", filename, line, search_for[i]);
found_count++;
}
}
if(found_it(use_or, found_count, search_len)) {
printf("%s\n", filename);
break;
} else {
found_count = 0;
}
}
free(line);
fclose(file);
return 0;
error:
if(line) free(line);
if(file) fclose(file);
return -1;
}
int parse_args(int *use_or, int *argc, char **argv[])
{
(*argc)--;
(*argv)++;
if(strcmp((*argv)[0], "-o") == 0) {
*use_or = 1;
(*argc)--; // skip the -o
(*argv)++;
check(*argc > 1, "You need words after -o.");
} else {
*use_or = 0;
}
return 0;
error:
return -1;
}
int main(int argc, char *argv[])
{
int i = 0;
int use_or = 1;
glob_t files_found;
check(argc > 1, "USAGE: logfind [-o] words");
check(parse_args(&use_or, &argc, &argv) == 0, "USAGE: logfind [-o] words");
check(list_files(&files_found) == 0, "Failed to list files.");
for(i = 0; i < files_found.gl_pathc; i++) {
scan_file(files_found.gl_pathv[i], use_or, argc, argv);
}
globfree(&files_found);
return 0;
error:
return 1;
}
读老师的函数一点一点来的补充:
1、strnlen:
在C语言中,strnlen
是一个用于求取字符串长度的函数,它的作用是返回一个字符串的长度,但不会超过指定的最大长度。
strnlen
函数的函数原型如下:
size_t strnlen(const char *str, size_t maxlen);
str
:要计算长度的字符串的指针。maxlen
:要计算的最大长度。
strnlen
函数会从str
指向的字符串开始计数,直到遇到字符串结尾的空字符\0
或者达到最大长度maxlen
,并返回实际的字符串长度。
与strlen
不同的是,strnlen
函数会限制计算的长度不超过maxlen
,这样可以避免访问超过字符串长度的内存,从而增加安全性。
示例用法:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello, World!";
size_t len1 = strnlen(str, 5);
size_t len2 = strnlen(str, sizeof(str));
printf("len1: %zu\n", len1);
printf("len2: %zu\n", len2);
return 0;
}
运行结果:
len1: 5
len2: 13
在上述示例中,strnlen(str, 5)
计算了str
的长度,但限制在最大长度为5,因此返回的长度为5。而strnlen(str, sizeof(str))
计算了str
的长度,没有设置最大限制,所以返回的长度为整个字符串的长度。
2、assert:
在C语言中,assert
是一个宏,用于在程序运行时检查一个表达式是否为真。如果表达式为假(即0),assert
宏会触发一个断言失败,并通过标准错误流输出一条错误消息,并终止程序的执行。
assert
的基本语法是:
#include <assert.h>
void assert(int expression);
其中,expression
是需要判断的条件表达式,如果为假则断言失败,输出错误消息。如果表达式为真,则assert
宏不做任何操作。
下面是一个使用assert
宏的示例:
#include <stdio.h>
#include <assert.h>
int divide(int x, int y){
assert(y != 0);
return x / y;
}
int main(){
printf("%d\n", divide(10, 2)); // 输出 5
printf("%d\n", divide(10, 0)); // 断言失败,程序终止
}
在上面的示例中,assert
宏用于判断除数y
是否为零。如果为零,则断言失败,程序终止,并输出一条错误消息。
需要注意的是,在发布版本的程序中,assert
宏会被宏定义NDEBUG
禁用,因此断言检查将被跳过,不会导致程序终止。只有在开发和调试阶段使用断言进行错误检查是有意义的。(老师得的dbg宏也同样如此!)
后语
1、这个项目的构思,由于纯粹自己构思,打翻了好几次想法。就像跟客户沟通一样,呵呵。
2、本来想放弃了,但是仔细一想,这个才是比较正式的学习笔记吧,干吧!
3、老师的程序读后感:
老师的程序就像一首诗!短短几行,让bug无处可藏!看那行首的预言吧!看看那整齐的check,看看error后面的if,怎能不让人佩服!代码虽然不多,却处处透着健壮,就像经常锻炼的人身上,没有一丝赘肉!老师的程序是一首诗,那是一首“稳健”的诗!
本以为就要出师,才知道差一点要“出事”。
范文没有用到目录函数,glob有这作用,那么问题来了,glob是不是用dirent编写的呢?
4、glob与dirent区别:
glob
函数并不是用dirent
函数编写的。glob
函数是C标准库中的一个函数,用于匹配文件路径名模式,并返回符合条件的文件名列表。
dirent
函数是用于遍历目录中的文件和子目录的函数,它可以用于获取目录中的文件名,并判断文件类型。
虽然glob
函数和dirent
函数都可以用于处理文件和目录,但它们的功能和用法是不同的。glob
函数是用于模式匹配文件名,它支持通配符,比如*
和?
,可以根据给定的模式匹配符合条件的文件名,并将这些文件名存放在一个glob_t
结构体中。
另一方面,dirent
函数是用于获取目录中的文件和子目录的函数,它提供了一组函数,如opendir
、readdir
和closedir
,用于打开和关闭目录,以及获取目录中的文件名和子目录名。
因此,虽然glob
函数和dirent
函数都涉及到文件和目录的处理,但它们是不同的函数,用途和实现方式也不相同。