C 语言通配符

C 语言中与字符串操作相关的函数。

转载至:https://jin-yang.github.io/post/program-c-string-linux-wildcard-introduce.html

简介

当 shell 在参数中遇到了通配符时,会尝试将其当作路径或文件名去在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行代换(路径扩展);否则就将该通配符作为一个普通字符传递给命令,然后再由命令进行处理。

也就是说,通配符是由 shell 处理的,在传递给可执行程序之前已经进行了处理,实际上就是一种 shell 实现的路径扩展功能。

’ * ‘ 匹配0或多个字符 a*b ab a012b aabcd
‘ ? ’ 匹配任意一个字符 a?b abb acb a0b
[list] 匹配list中的任意单一字符 a[xyz]b axb ayb azb
[!list] 匹配除list中的任意单一字符 a[!0-9]b axb abb a-b
[c1-c2] 匹配c1到c2中的任意单一字符 a[0-9]b a0b a9b
{string1,string2,…} 匹配sring1或string2(或更多)其一字符串
另外,需要注意 shell 通配符和正则表达式之间的区别。

fnmatch()

就是判断字符串是不是符合 pattern 所指的结构,这里的 pattern 是 shell wildcard pattern,其中部分匹配行为可以通过 flags 配置,详见 man 3 fnmatch 。

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <fnmatch.h>
#include <sys/types.h>

int main(void)
{
	int rc;
	DIR *dir;
	struct dirent *entry;
	const char *pattern = "*.log";

	dir = opendir("/tmp");
	if (dir == NULL) {
		perror("opendir()");
		exit(EXIT_FAILURE);
	}
	while ((entry = readdir(dir)) != NULL) { // 逐个获取文件夹中文件
		rc = fnmatch(pattern, entry->d_name, FNM_PATHNAME | FNM_PERIOD);
		if (rc == 0) {         // 符合pattern的结构
			printf("match %s\n", entry->d_name);
		} else if (rc == FNM_NOMATCH){
			continue ;
		} else {
			printf("error %d file=%s\n", rc, entry->d_name);
		}
	}
	closedir(dir);
	return 0;
}

wordexp()

按照 Shell-Style Word Expansion 扩展将输入字符串拆分,返回的格式为 wordexp_t 变量,其中包括了三个变量,两个比较重要的是:A) we_wordc 成员数;B) we_wodv 数组。

注意,在解析时会动态分配内存,所以在执行完 wordexp() 后,需要执行 wordfree();另外,如果遇到内存不足会返回 WRDE_NOSPACE 错误,此时可能已经分配了部分地址,所以仍需要执行 wordfree() 。

  1. 按照空格解析;2) 正则表达式;3) 环境变量。

#include <stdio.h>
#include <stdlib.h>
#include <wordexp.h>

int main(void)
{
int i, ret;
wordexp_t p;

ret = wordexp("foo bar $SHELL *[0-9].c *.c", &p, 0);
if (ret == WRDE_NOSPACE) {
	wordfree(&p);
	exit(EXIT_FAILURE);
} else if (ret != 0) {
	exit(EXIT_FAILURE);
}
for (i = 0; i < p.we_wordc; i++)
	printf("%s\n", p.we_wordv[i]);
wordfree(&p);

return 0;

}

glob

对于通配符的匹配,还可以使用 glob() 函数。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <glob.h>
#include <sys/stat.h>
#include <sys/types.h>

int main(void)
{
        glob_t globbuf;
        struct stat statbuf;
        int rc;
        size_t pathc;
        char **pathv;

        rc = glob("/var/run/haproxy[0-9]*.sock", GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
        if (rc != 0) {
                if (rc == GLOB_NOMATCH)
                        return 0;
                else
                        return -1;
        }

        pathv = globbuf.gl_pathv;
        pathc = globbuf.gl_pathc;
        printf("Got #%zd matches\n", pathc);

        for (; pathc-- > 0; pathv++) {
                rc = lstat(*pathv, &statbuf);
                if (rc < 0 || !S_ISSOCK(statbuf.st_mode))
                        continue;
                printf("Match path: %s\n", *pathv);
        }

        globfree(&globbuf);
        return 0;
}

qsort()

只用于数组的排序,对于链表等无效。

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void , const void));
base : 数组的基地址
nitems: 数组包含的元素;
size : 每个元素的大小;
compar: 比较函数;
示例程序如下。

#include <stdio.h>
#include <stdlib.h>

int cmpfunc(const void *a, const void *b)
{
	return (*(int*)a - *(int*)b);
}

int main(void)
{
	int n;
	int values[] = { 88, 56, 100, 2, 25 };

	printf("Before sorting the list is: \n");
	for (n = 0 ; n < 5; n++)
		printf("%d ", values[n]);
	putchar('\n');
	qsort(values, 5, sizeof(int), cmpfunc);
	printf("After sorting the list is: \n");
	for( n = 0 ; n < 5; n++ )
		printf("%d ", values[n]);
	putchar('\n');

	return(0);
}

最佳实践
如果只想匹配一些通配符文件,那么就可以将 flag 设置为 WRDE_NOCMD | WRDE_UNDEF 以防止一些非法的入参。通过 wordexp() 过滤通配符文件,然后可以通过 qsort() 进行排序,示例如下。

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <wordexp.h>
#include <sys/stat.h>

static int config_cmp_string(const void * a, const void * b)
{
        return strcmp(*(char **)a, *(char **)b);
}

int main(void)
{
	int rc, i;
	wordexp_t we;
	struct stat statinfo;
	const char *pattern = "/tmp/*.log", *path;

	rc = wordexp(pattern, &we, WRDE_NOCMD | WRDE_UNDEF);
	if (rc != 0) {
		fprintf(stderr, "wordexp(%s) failed, rc %d.\n", pattern, rc);
		exit(EXIT_FAILURE);
	}
	qsort((void *)we.we_wordv, we.we_wordc, sizeof(*we.we_wordv), config_cmp_string);

	for (i = 0; i < (int)we.we_wordc; i++) {
		path = we.we_wordv[i];
		if (stat(path, &statinfo) != 0) {
			fprintf(stderr, "stat(%s) failed, %s.\n", path, strerror(errno));
			continue;
		}

		if (S_ISREG(statinfo.st_mode)) {
			fprintf(stdout, "regular file: %s\n", path);
		} else if (S_ISDIR(statinfo.st_mode)) {
			fprintf(stdout, "directory: %s\n", path);
		}
	}
	wordfree(&we);

	return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值