forked from xqmmcqs/BUPT-Projects
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Lab2.tex
368 lines (308 loc) · 10.3 KB
/
Lab2.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
\documentclass[lang=cn,11pt,a4paper,cite=authornum]{paper}
\title{Linux开发环境及应用 上机作业二:遍历目录 \\ 实验报告}
\author{毛子恒 \\ 2019211397}
\institute{北京邮电大学\ 计算机学院}
\date{\zhtoday}
\setmonofont{Consolas}
% 本文档命令
\nocite{*}
\begin{document}
\maketitle
\section{实验内容}
编程实现程序\mintinline{text}{list.c},列表普通磁盘文件,包括文件名和文件大小。
\begin{enumerate}
\item 使用vi编辑文件,熟悉工具vi。
\item 使用Linux的系统调用和库函数。
\item 体会Shell文件通配符的处理方式以及命令对选项的处理方式。
\end{enumerate}
\section{实验步骤}
\paragraph{命令行选项获取}
实现一个类似于Linux的\mintinline{text}{getopt}函数\mintinline{text}{getOpt},用于获取命令行选项,该函数依次遍历命令行参数,将获取到的选项返回。全局变量\mintinline{text}{optind}和\mintinline{text}{optarg}分别用于表示遍历到的参数下标以及获取到的参数值。
相比较\mintinline{text}{getopt}函数,我实现的函数稍作简化,无法通过传入参数定制需要解析的命令行选项,但是可以获取到匿名的参数(也就是路径选项)。
\paragraph{打开目录}
\mintinline{text}{match}函数尝试打开传入的路径,并且根据传入的路径是否以'/'结尾做了一些特殊处理,如果指定的路径可以打开,那么调用\mintinline{text}{output}函数输出。
\paragraph{输出}
\mintinline{text}{output}函数判断指定的文件是否满足筛选条件,如果满足则输出,其中还涉及递归输出的过程。
\paragraph{getopt\_long函数的使用}
\mintinline{text}{getopt_long}函数声明如下:
\mintinline{C}{int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);}
\mintinline{text}{getopt_long}接受长、短的命令行选项,长选项以\mintinline{text}{--}开头。如果程序只接受长选项,那么\mintinline{text}{optstring}应指定为空字符串。如果缩写是唯一的,那么长选项名称可以缩写。长选项可以采用两种形式:\mintinline{text}{--arg=param}或\mintinline{text}{--arg param}。\mintinline{text}{longopts}是结构体\mintinline{text}{option}的数组。
结构体\mintinline{text}{option}的声明如下:
\begin{code}
\begin{minted}{C}
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
\end{minted}
\end{code}
\begin{itemize}
\item \mintinline{text}{name}:长选项的名字。
\item \mintinline{text}{has_arg}:0,不需要参数;1,需要参数;2,参数是可选的。
\item \mintinline{text}{flag}:指定如何为长选项返回结果。如果是NULL,那么函数返回\mintinline{text}{val}(设置为等效的短选项字符),否则返回0。
\item \mintinline{text}{val}:要返回的值。
\end{itemize}
\mintinline{text}{logopts}数组的最后一个元素必须用零填充。
当一个短选项字符被识别时,\mintinline{text}{getopt_long}也返回选项字符。
\section{代码}
\begin{code}
\begin{minted}{C}
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#ifndef MAXNAMLEN
#define MAXNAMLEN 255
#endif // MAXNAMLEN
char *opt_arg;
int opt_ind = 1;
struct Flags
{
bool recursive;
bool all;
long low;
long high;
double modified;
} flags;
char pwd[MAXNAMLEN];
int getOpt(int argc, char *argv[]);
bool match(const char *pattern);
void output(const char *path);
void help_info();
int main(int argc, char *argv[])
{
int path_args_cnt = 0;
char **path_args_list = (char **)malloc(argc * sizeof(char *));
flags.high = flags.modified = 0x7fffffff;
int ch;
while ((ch = getOpt(argc, argv)) != -1)
{
switch (ch)
{
case 0:
path_args_list[++path_args_cnt] = opt_arg;
break;
case 'r':
flags.recursive = true;
break;
case 'a':
flags.all = true;
break;
case 'l':
flags.low = atoll(opt_arg);
if (strcmp(opt_arg, "0") && !flags.low)
help_info();
break;
case 'h':
flags.high = atoll(opt_arg);
if (strcmp(opt_arg, "0") && !flags.high)
help_info();
break;
case 'm':
flags.modified = atoll(opt_arg) * 3600 * 24;
if (strcmp(opt_arg, "0") && !flags.modified)
help_info();
break;
case '?':
help_info();
break;
}
}
if (getcwd(pwd, MAXNAMLEN) == NULL)
{
fprintf(stderr, "getcwd(): Error.");
free(path_args_list);
exit(1);
}
if (!path_args_cnt) // no path args, list pwd
{
match(pwd);
free(path_args_list);
return 0;
}
for (int i = 1; i <= path_args_cnt; ++i)
{
char *pattern = (char *)malloc((strlen(path_args_list[i]) + MAXNAMLEN + 1) * sizeof(char));
if (!strlen(path_args_list[i]) || (path_args_list[i][0] == '.' && path_args_list[i][1] != '.'))
{
strcpy(pattern, pwd);
strcpy(pattern + strlen(pwd), path_args_list[i] + 1);
}
else if ((path_args_list[i][0] == '.' && path_args_list[i][1] == '.' && path_args_list[i][2] == '/') || strrchr(path_args_list[i], '/') == NULL)
{
strcpy(pattern, pwd);
pattern[strlen(pwd)] = '/';
strcpy(pattern + strlen(pwd) + 1, path_args_list[i]);
}
else
strcpy(pattern, path_args_list[i]);
// printf("%s\n", pattern);
if (!match(pattern))
fprintf(stderr, "list: cannot access '%s': No such file or directory\n", path_args_list[i]);
free(pattern);
}
free(path_args_list);
return 0;
}
int getOpt(int argc, char *argv[])
{
if (opt_ind >= argc)
return -1;
if (argv[opt_ind][0] != '-')
{
opt_arg = argv[opt_ind];
opt_ind++;
return 0;
}
if (argv[opt_ind][1] == '-')
{
opt_ind++;
return -1;
}
else if (argv[opt_ind][1] == 'r' || argv[opt_ind][1] == 'a')
{
opt_arg = NULL;
char opt = argv[opt_ind][1];
opt_ind++;
return opt;
}
else if (argv[opt_ind][1] == 'l' || argv[opt_ind][1] == 'h' || argv[opt_ind][1] == 'm')
{
if (opt_ind + 1 >= argc)
{
opt_arg = NULL;
return '?';
}
else
opt_arg = argv[opt_ind + 1];
char opt = argv[opt_ind][1];
opt_ind += 2;
return opt;
}
else
{
opt_ind++;
return '?';
}
}
bool match(const char *pattern)
{
char *last_slash = strrchr(pattern, '/');
char *last_pattern = (char *)malloc(strlen(pattern) * sizeof(char));
char *file_name = (char *)malloc(MAXNAMLEN * sizeof(char));
strncpy(last_pattern, pattern, last_slash - pattern);
strcpy(file_name, last_slash + 1);
DIR *dir;
struct dirent *ent;
if ((dir = opendir(last_pattern)) == NULL)
{
free(last_pattern);
free(file_name);
return false;
}
if (!strlen(file_name)) // pattern end with '/'
{
*last_slash = 0;
last_slash = strrchr(last_pattern, '/');
*last_slash = 0;
strcpy(file_name, last_slash + 1);
closedir(dir);
dir = opendir(last_pattern);
}
// printf("%s %s\n", last_pattern, file_name);
while ((ent = readdir(dir)) != NULL)
{
if (!strcmp(file_name, ent->d_name))
{
output(pattern);
return true;
}
}
closedir(dir);
free(last_pattern);
free(file_name);
return false;
}
void output(const char *path)
{
struct stat statbuf;
stat(path, &statbuf);
if (S_ISDIR(statbuf.st_mode))
{
DIR *dir;
struct dirent *ent;
struct stat statbuf;
if ((dir = opendir(path)) == NULL)
{
fprintf(stderr, "Can`t open directory %s\n", path);
return;
}
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_name[0] == '.')
if (!flags.all)
continue;
char *new_path = (char *)malloc((strlen(path) + MAXNAMLEN + 1) * sizeof(char));
strcpy(new_path, path);
if (!strcmp(path, "/"))
strcpy(new_path + 1, ent->d_name);
else
{
strcpy(new_path + strlen(path) + 1, ent->d_name);
*(new_path + strlen(path)) = '/';
}
stat(new_path, &statbuf);
if (statbuf.st_size >= flags.low && statbuf.st_size <= flags.high && difftime(time(NULL), statbuf.st_mtime) <= flags.modified)
printf("%10ld %s\n", statbuf.st_size, new_path);
if (!flags.recursive)
{
free(new_path);
continue;
}
if (S_ISDIR(statbuf.st_mode))
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
continue;
output(new_path);
}
free(new_path);
}
}
else
{
stat(path, &statbuf);
if (statbuf.st_size >= flags.low && statbuf.st_size <= flags.high && difftime(time(NULL), statbuf.st_mtime) <= flags.modified)
printf("%10ld %s\n", statbuf.st_size, path);
}
}
void help_info()
{
printf("usage: list [-r] [-a] [-l <minimum_size>] [-h <maximum_size>] [-m <modified_days>] [file ...]\n");
exit(1);
}
\end{minted}
\end{code}
\section{运行结果}
采用如下命令编译:
\begin{code}
\begin{minted}{shell}
gcc list.c -o list -std=c11 -Wall
\end{minted}
\end{code}
运行结果如\figref{fig:p1}。
\begin{figure}[!htb]
\centering
\includegraphics[width=\textwidth]{./images/l2.jpg}
\caption{运行结果\label{fig:p1}}
\end{figure}
\section{实验总结}
实验期间我观察到Shell会先对通配符进行解析,将其展开为多个参数传递给程序。
本次实验中我应用Linux目录和文件信息访问的库函数,实现了对指定目录的遍历操作,使我对Linux的文件系统理解更加深刻。
\end{document}