`
stride
  • 浏览: 59103 次
  • 性别: Icon_minigender_1
  • 来自: 长春
社区版块
存档分类
最新评论

在windows下编写tail命令,提供源码和exe下载

阅读更多

本程序编写于32位win7旗舰版,测试也是在这个系统下,欢迎朋友们提出修改意见。

 

功能:

显示指定文本文件的最后n行,支持自动读取及手动按回车读取。

 

格式说明:

tail <fileName> [-b <Number>] [-l <Number>] [-f <Number>] 

 

标志说明:

-b <Number>

从末尾倒数 Number 表示的 512 字节块位置开始读取指定文件(无此参数则默认最后512字节),若要读取大量文本请将此值调大,例如:tail -b 10 filename.txt

-l <Number>

显示末尾Number行(无此参数则默认显示最后30行)

-f <Number>

每隔多少秒自动显示(无此参数则手动按回车显示下一次)

 

下面是调用例子:

 

tail -f 1 -b 2 -l 10 t.txt  

每一秒自动显示一次(-f 1),最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后10行(-l 10)

 

tail -b 2 -l 50 t.txt

最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后50行(-l 50),手动按回车显示(省略[-f])

 

tail -b 2 t.txt

最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后30行(省略[-l Number]默认读取最后30行),手动按回车显示(省略[-f])

 

#include <windows.h>
#include <iostream>  
#include <fstream>
#include <string>
#include <malloc.h>
#include <stdlib.h>   

using namespace std;

/**
 * 格式说明:
 *	tail <fileName> [-b <Number>] [-l <Number>] [-f <Number>] 
 *
 * 标志说明:
 *	-b <Number>
 *		从末尾倒数 Number 表示的 512 字节块位置开始读取指定文件(无此参数则默认最后512字节)
 *	-l <Number>
 *		显示末尾Number行(无此参数则默认显示最后30行)
 *	-f <Number>
 *		每隔多少秒自动显示(无此参数则手动按回车显示下一次)
 * 
 * 例:
 *	tail -f 1 -b 2 -l 10 t.txt  
 *	每一秒自动显示一次(-f 1),最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后10行(-l 10)
 *
 *	tail -b 2 -l 50 t.txt
 *	最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后50行(-l 50),手动按回车显示(省略[-f])
 * 
 *	tail -b 2 t.txt
 *	最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后30行(省略[-l Number]默认读取最后30行),手动按回车显示(省略[-f])
 */

// 检查字符数组的内容是否是标志
bool isFlag(char * charArr)
{
	string temp = charArr;
	if(charArr[0] == '-')
	{
		// 是以减号开头
		if(temp!="-b" && temp!="-f" && temp!="-l")
		{
			// 非法标志
			return false;
		}
		return true;
	}
	else
		return false;
}
// 检查字符数组的内容是否是正整数,不是正整数则返回零
int isNum(char * charArr)
{
	try
	{
		int v = atoi(charArr);
		// 将当前参数转整数
		if( atoi(charArr) < 1 )
			return 0;
		return v;
	}
	catch(...)
	{
		return 0;
	}
}

/**
 * 检查参数是否正确
 * @param argc 一共有几个参数(从1开始)
 * @param arg 用户输入的参数
 *
 * @return int (1:验证通过|-1:验证失败)
 */
int checkArg(int argc, char ** arg, int * b, int * l, int * f, char ** filename)
{
	// 返回值
	int ret = 1;

	// 前一个参数是否是标志
	bool preArgIsFlag = false;

	// 是否已经读到文件名
	bool readedFileName = false;

	// 是否已经读到-b
	bool readedB = false;

	// 是否已经读到-l
	bool readedL = false;
	
	// 是否已经读到-f
	bool readedF = false;
	
	// 一维数组下标
	int x = 0, v = 0;

	string temp, temp1, temp2;

	// 按元素个数从1开始循环
	for(int i=1; i<argc; i++)
	{
		temp1 = arg[i];
		if(i < argc-1)
		{
			temp2 = arg[i+1];
		}
		// 检查当前循环到的是不是最后一个元素
		if(i < argc-1) // 如果当前循环到的不是最后一个元素
		{
			if(isFlag(arg[i])) // 如果当前元素是标志
			{
				if(!isFlag(arg[i+1])) // 如果下一个元素不是标志
				{
					// 如果当前元素是标志、下一个元素不是标志
					v = isNum(arg[i+1]);
					if(v < 1)
						return 0;
					if(temp1 == "-b")
					{
						if(readedB) // 如果已经读到-b则返加零
							return 0;
						*b = v;
						readedB = true;
					}
					else if(temp1 == "-l")
					{
						if(readedL) // 如果已经读到-l则返加零
							return 0;
						*l = v;
						readedL = true;
					}
					else if(temp1 == "-f")
					{
						if(readedF) // 如果已经读到-f则返加零
							return 0;
						*f = v;
						readedF = true;
					}else{ // 非法标志
						return 0;
					}
					i++;
				}
				else // 如果下一个元素是标志
				{
					return 0;
				}
			}else{ // 如果当前元素不是标志则认定当前元素是文件名
				if(readedFileName) // 如果已经读到文件名则返回零
					return 0;
				strcpy(*filename, arg[i]); // 修改传入参数中的文件名
				readedFileName = true;
			}
		} // if(i < argc-1) // 如果当前循环到的不是最后一个元素
		else // 如果当前循环到的是最后一个元素
		{
			if(isFlag(arg[i])){ // 如果当前元素是标志
				return 0;
			}
			else // 如果当前元素不是标志
			{
				if(readedFileName) // 如果已经读到文件名则返回零
					return 0;
				strcpy(*filename, arg[i]); // 修改传入参数中的文件名
				readedFileName = true;
			}
		}
	} // for(int i=1; i<argc; i++)

	if(!readedB) // 如果没有读到-b则赋默认值
	{
		*b = 1;
	}
	if(!readedL) // 如果没有读到-l则赋默认值
	{
		*l = 30;
	}
	if(!readedF) // 如果没有读到-f则赋默认值
	{
		*f = 0;
	}
	// 判断是否有文件名
	if(!readedFileName)
		return 0;

	return ret;
}

int main(int argc, char ** argv)
{
	// 
	int b; // 缓冲区大小(是几个512)
	int l; // 要末尾读多少行
	int f; // 每隔几秒自动显示
	char * filename = new char[512];

	// 检查参数是否正确
	int result = checkArg(argc, argv, &b, &l, &f, &filename);
	if(!result)
	{
		printf("出现错误,请参照以下说明修改命令参数:\n");
		printf("格式说明:\n");
		printf("	tail <fileName> [-b <Number>] [-l <Number>] [-f <Number>] \n");
		printf("	其中fileName最长512字节\n\n");
		printf("标志说明:\n");
		printf("	-b <Number>\n");
		printf("		从末尾倒数 Number 表示的 512 字节块位置开始读取指定文件(无此参数则默认最后512字节)\n");
		printf("	-l <Number>\n");
		printf("		显示末尾Number行(无此参数则默认显示最后30行)\n");
		printf("	-f <Number>\n");
		printf("		每隔多少秒自动显示(无此参数则手动按回车显示下一次)\n\n");
		printf("例:\n");
		printf("	tail -f 1 -b 2 -l 10 t.txt  \n");
		printf("	每一秒自动显示一次(-f 1),最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后10行(-l 10)\n\n");
		printf("	tail -b 2 -l 50 t.txt\n");
		printf("	最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后50行(-l 50),手动按回车显示(省略[-f])\n\n");
		printf("	tail -b 2 t.txt\n");
		printf("	最多从末尾读取512*2个字节(-b 2),从这些字节中读取最后30行(省略[-l Number]默认读取最后30行),手动按回车显示(省略[-f])\n");
		return 1;
	}

	// 要显示多少行
	int showCount = l, showLineCount;

	// 文件总大小
	int count;
	char * charArr;
	while(true)
	{
		showLineCount = showCount;
		ifstream ifs;  
		// 按二进制读取文本
		ifs.open (filename, ios::binary);

		if(ifs.fail())
		{
			cout<<"错误:文件不存在。"<<endl;
			return 1;
		}

		// 将文件指针移动到末尾
		ifs.seekg (0, ios::end);
		
		// 读取文件总大小
		count = ifs.tellg();
		
		// 根据文件总大小计算应该读取多少字节
		int shouldReadLength = (count>=512*b)?512*b:count;

		// 按指定大小分配内存,加1是为了能在最后添加结束符'\0'
		charArr = new char[shouldReadLength+1];

		int i;
		// 已读取字节数
		int readed = 0;

		// 将文件指针移动到距末尾-shouldReadLength个字节的位置
		ifs.seekg (-shouldReadLength, ios::end);

		// 读取指定大小的内容
		ifs.read (charArr,shouldReadLength);

		// 添加结束符
		charArr[shouldReadLength] = '\0';

		for(i=shouldReadLength-1; i>=0 && showLineCount>0; i--,readed++)
		{
			if(charArr[i] == '\n'){
				showLineCount--;
			}
		}

		// 输出

		printf("%s\n", charArr + shouldReadLength - readed); // 这种方法可以输出到控制台
		//cout.write (charArr + shouldReadLength - readed,readed);// 这种方法也可以输出到控制台
		
		// 释放字符数组的内存空间
		delete [] charArr;
		// 关闭文件流
		ifs.close();

		if(f==0) // 用户没有输入-f参数,等待用户再次敲回车
		{
			// 如果用手动的就用cin---------- 开始
			cout<<"按Ctrl+C退出或按回车继续:";
			// 用户按回车时进行下一次显示
			cin.get();
			//system("PAUSE");
			// 如果用手动的就用cin---------- 结束
		}
		else // 用户输入了-f参数,按用户输入的秒数睡眠
		{
			// 如果要自动的就用Sleep-------- 开始
			Sleep(1000*f);
			// 如果要自动的就用Sleep-------- 结束
		}

	}

	return 0;
}
 

 

附件tail.rar中是exe可执行文件,下面是校验码:

大小: 66070 字节

修改时间: 2011年8月10日, 15:19:51

MD5: 0CAB365E4A42F83FDABAA5AB011DC0A9

SHA1: 3F0972645F15BA6DB8F70A7EF3F9C760607B684B

CRC32: 8F22B704

 

转载请注明出处

分享到:
评论
1 楼 stride 2011-08-10  
原来atoi函数不抛异常,那个try写多余了

相关推荐

Global site tag (gtag.js) - Google Analytics