在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
转载请注明出处
上一篇: 用shell压缩三个月及之前的文件(文件名含日期)
下一篇: shell编程:expr正则匹配心得1