linux上面的程序刚开始启动的时候一般会从命令行获取某些参数,比如以守护进程运行啊什么的,典型的例子就是linux下的man,如下图所示



实现该功能可以使用getopt函数实现,该函数在头文件unistd.h定义

函数原型

int getopt(int argc,char * const argv[ ],const char * optstring);

函数说明

getopt()用来分析命令行参数。参数argc和argv是由main()传递的参数个数和内容。参数optstring 则代表欲处理的选项字符串。此函数会返回在argv 中下一个的选项字母,此字母会对应参数optstring 中的字母。如果选项字符串里的字母后接着冒号“:”,则表示还有相关的参数,全域变量optarg 即会指向此额外参数。如果getopt()找不到符合的参数则会印出错信息,并将全域变量optopt设为“?”字符,如果不希望getopt()印出错信息,则只要将全域变量opterr设为0即可。

短参数的定义

getopt()使用optstring所指的字串作为短参数列表,象”1ac:d::”就是一个短参数列表。短参数的定义是一个’-‘后面跟一个字母或数字,象-a, -b就是一个短参数。每个数字或字母定义一个参数。

  其中短参数在getopt定义里分为三种:

  1. 不带值的参数,它的定义即是参数本身。

  2. 必须带值的参数,它的定义是在参数本身后面再加一个冒号。

  3. 可选值的参数,它的定义是在参数本身后面加两个冒号 。

  在这里拿上面的”1ac:d::”作为样例进行说明,其中的1,a就是不带值的参数,c是必须带值的参数,d是可选值的参数。

  在实际调用中,’-1 -a -c cvalue -d’, ‘-1 -a -c cvalue -ddvalue’, ‘-1a -ddvalue -c cvalue’都是合法的。这里需要注意三点:

  1. 不带值的参数可以连写,象1和a是不带值的参数,它们可以-1 -a分开写,也可以-1a或-a1连写。

  2. 参数不分先后顺序,’-1a -c cvalue -ddvalue’和’-d -c cvalue -a1’的解析结果是一样的。

  3. *要注意可选值的参数的值与参数之间不能有空格*,必须写成-ddvalue这样的格式,如果写成-d dvalue这样的格式就会解析错误。

返回值

  getopt()每次调用会逐次返回命令行传入的参数。

  当没有参数的最后的一次调用时,getopt()将返回-1。

  当解析到一个不在optstring里面的参数,或者一个必选值参数不带值时,返回’?’。

  当optstring是以’:’开头时,缺值参数的情况下会返回’:’,而不是’?’ 。

  读取命令行参数函数可如下设计:

 

// 解析命令行参数
//getopt为标准库的函数,判断传进来的参数是否是后面参数集合的一部分
while ((ch = getopt(argc, (char* const*)argv, "vhd")) != -1)
{//这里要做一个类型转换,否则出错
switch(ch)
{
case 'v'://获取版本
version();
break;
case 'd'://守护进程
daemonized = 1;
break;
case 'h'://获取帮助
case '?':
default:
usage();
}
}

根据返回值跳转到不同的函数执行。

日志宏

下面说说日志宏的实现,日志对于服务器程序或其他长期运行的,以守护进程方式运行的程序来说是非常重要的,除了发生错误的时候查看,在开发的时候输出调试信息更是有利于我们的开发,应该有不少的人会觉得用日志信息来调试会比使用gdb调试好。muduo库里面封装了很好的日志类,并且支持日志滚动,高效异步日志,实现了一个较好的日志功能,当然,我们并不用也去开发一个那样强大的日志库,“不要重复发明轮子”,学习是可以的,因为这个爬虫并不是用muduo库写的,而且我们的要求并没有那么高,所以我们可以自己实现一个简单的日志功能。

个人觉得日志一般应该包括时间,文件,所在行,日志等级,以及我们自己要输出的信息

一个简单的日志宏可以如下实现:

#define MAX_MESG_LEN   1024

#define SPIDER_LEVEL_DEBUG 0
#define SPIDER_LEVEL_INFO 1
#define SPIDER_LEVEL_WARN 2
#define SPIDER_LEVEL_ERROR 3
#define SPIDER_LEVEL_CRIT 4 static const char * LOG_STR[] = {
"DEBUG",
"INFO",
"WARN",
"ERROR",
"CRIT"
}; extern Config *g_conf; //可变参数
//输出日期,时间,日志级别,源码文件,行号,信息
//'\'后面不要加注释
#define SPIDER_LOG(level, format, ...) do{ \
if (level >= g_conf->log_level) {\
time_t now = time(NULL); \
char msg[MAX_MESG_LEN]; \
char buf[32]; \
sprintf(msg, format, ##__VA_ARGS__); \
strftime(buf, sizeof(buf), "%Y%m%d %H:%M:%S", localtime(&now)); \
fprintf(stdout, "[%s] [%s] [file:%s] [line:%d] %s\n", buf, LOG_STR[level],__FILE__,__LINE__, msg); \
fflush (stdout); \
}\
if (level == SPIDER_LEVEL_ERROR) {\
exit(-1); \
} \
} while(0)

fprintf(stdout, “[%s] [%s] [file:%s] [line:%d] %s\n”, buf, LOG_STR[level],FILE,LINE, msg);这个是核心的输出函数,从左到右一次输出时间,日志等级,文件,所在行号,以及我们的信息。

VA_ARGS是一个可变参数宏,可以帮助我们把我们要输出的信息按照我们所要求的特定格式写进数组msg中,可见这里

FILELINE(前后都是双下划线,这里显示不出来)用于获取文件和行号

日志宏不仅是只能输出到终端,即一般日志应该能够写到文件中,特别是在以守护进程方式运行的时候,我们不希望终端上出现任何的消息,而是需要的时候采取查看日志文件,这里可以简单地实现下这个功能。

在以守护进程方式运行的时候,使用dup2函数将日志文件的文件描述符复制到标准输出stdout中,这样一来,输出到stdout的内容都将写到文件中,当然我们这里只是为了实现这个功能而已,效率及其他不做考虑,大并发的日志设计可以参考muduo库

该日志的输出效果如下:

一只简单的网络爬虫(基于linux C/C++)————读取命令行参数及日志宏设计的更多相关文章

  1. 一只简单的网络爬虫(基于linux C/C++)————开篇

    最近学习开发linux下的爬虫,主要是参考了该博客及其他一些网上的资料.网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息 ...

  2. 一只简单的网络爬虫(基于linux C/C++)————主事件流程

    该爬虫的主事件流程大致如下: 1.获取命令行参数,执行相应操作 2.读取配置文件,解析得到各种设置 3.载入各种模块 4.种子入队,开启DNS解析线程(原始队列不为空时解析) 5.创建epoll,开启 ...

  3. 一只简单的网络爬虫(基于linux C/C++)————浅谈并发(IO复用)模型

    Linux常用的并发模型 Linux 下设计并发网络程序,有典型的 Apache 模型( Process Per Connection ,简称 PPC ), TPC ( Thread Per Conn ...

  4. 一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载

    插件在软件设计中有很大的好处,可以方便地扩展各种功能,使用插件技术能够在分析.设计.开发.项目计划.协作生产和产品扩展等很多方面带来好处: (1)结构清晰.易于理解.由于借鉴了硬件总线的结构,而且各个 ...

  5. 一只简单的网络爬虫(基于linux C/C++)————socket相关及HTTP

    socket相关 建立连接 网络通信中少不了socket,该爬虫没有使用现成的一些库,而是自己封装了socket的相关操作,因为爬虫属于客户端,建立套接字和发起连接都封装在build_connect中 ...

  6. 一只简单的网络爬虫(基于linux C/C++)————守护进程

    守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动, ...

  7. 一只简单的网络爬虫(基于linux C/C++)————利用正则表达式解析页面

    我们向一个HTTP的服务器发送HTTP的请求后,服务器会返回可能一个HTML页面(当然也可以是其他的资源),我们可以利用返回的HTML页面,在其中寻找其他的Url,例如我们可以这样在浏览器上查看一下H ...

  8. 一只简单的网络爬虫(基于linux C/C++)————线程相关

    爬虫里面采用了多线程的方式处理多个任务,以便支持并发的处理,把主函数那边算一个线程的话,加上一个DNS解析的线程,以及我们可以设置的max_job_num值,最多使用了1+1+max_job_num个 ...

  9. 一只简单的网络爬虫(基于linux C/C++)————Url处理以及使用libevent进行DNS解析

    Url处理 爬虫里使用了两个数据结构来管理Url 下面的这个数据结构用来维护原始的Url,同时有一个原始Url的队列 //维护url原始字符串 typedef struct Surl { char * ...

随机推荐

  1. Java第十六天,list接口

    List接口 1.三大特点: ① 有序.② 有索引. ③ 允许存在重复元素. 注意: ① 利用list接口的索引执行操作时,要防止索引越界引起的程序错误. 2.基本使用: 针对List接口有索引的特点 ...

  2. 2020 PHP 初级 / 基础面试题,祝你金三银四跳槽加薪 (适合基础不牢固的 PHPer)

    1.PHP 语言的一大优势是跨平台,什么是跨平台? PHP 的运行环境最优搭配为 Apache+MySQL+PHP,此运行环境可以在不同操作系统(例如 windows.Linux 等)上配置,不受操作 ...

  3. lr具体使用步骤概述

    lr具体使用 1 无工具情况下的性能测试 2性能测试工具LoadRunner的工作原理 3 VuGen应用介绍 4 协议的类型及选择方法 5 脚本的创建过程 6 脚本的参数化 7 调试技术 8 Con ...

  4. docker-compose 基于Dockerfile 安装并启动redis容器的血案

    前言 为了实现"一键部署"的目的,我采用Dockerfile 和 docker-compose来实现自己的目的.这个过程中,我怎么也无法启动自己的redis-server服务. 目 ...

  5. I/O流之--转换流:InputStreamReader 和InputStreamWriter

    I/O流之--转换流:InputStreamReader 和InputStreamWriter 分类: java2014-07-01 15:30 815人阅读 评论(0) 收藏 举报   目录(?)[ ...

  6. Python队列的三种队列方法

    今天讲一下队列,用到一个python自带的库,queue 队列的三种方法有: 1.FIFO先入先出队列(Queue) 2.LIFO后入先出队列(LifoQueue) 3.优先级队列(PriorityQ ...

  7. ViewStub

    在书上了解了ViewStub,但只是带过两笔,没能了解.在网上搜索了一些资料,虽然很多文章都讲得比较完善,但是觉得可能是表达的原因,导致某个点上我理解错误,慢慢的才算比较清楚的认识ViewStub. ...

  8. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(七)之Access Control

    Access control ( or implementation hiding) is about "not getting it right the first time." ...

  9. 选择IT行业的自我心得,希望能帮助到各位!(二)

    在前面说道的一,希望大家能喜欢,这也只是自己的一种经历,每个人都有年轻的时候,谁的年级都有自以为是,谁的年轻都有风华正茂,谁的年轻都让自己的内涵给我们自己摔了一个狠狠的道理,人外有人天外有天.我记得当 ...

  10. python画图——雪花(科赫曲线)

    科赫曲线是一种分形,其形态非常像雪花,因此又被称作科赫雪花.雪花曲线. 下面是用python的turtle包让我们来实时画一个 import turtledef koch(t,n): #定义一个函数 ...