欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-24 19:05:56
...

本程序编写于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

 

转载请注明出处