65.9K
CodeProject 正在变化。 阅读更多。
Home

C#.NET 中实现 Linux "tail -f" 命令

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.78/5 (8投票s)

2006年8月31日

viewsIcon

64444

downloadIcon

979

打印文件最后 10 行。

引言

这是一个非常基础的实用工具,你肯定需要它。我最初使用的是 GNU 工具 for Windows,但后来我想用 .NET 编写这些工具,所以 tail.exe 是第一个。

我查阅了许多 .NET 中 tail 命令的实现,但它们都不是 GNU 对应版本的完整移植。

代码

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
using System.Threading;

namespace GNUtools
{
    class Program
    {
        static int Main(string[] args)
        {
            int noOfLines = 0;
            int returnStatus = 0;
            string newline = "\n";//Environment.NewLine;??
            int charSize = encoding.IsSingleByte ? 1 : 2;
            byte[] buffer = null;
            bool printed = false;
            string temp = string.Empty;

            ParseArgs(args);

            FileStream stream=null;
            try
            {
                stream = new FileStream(fileName, FileMode.Open, 
					FileAccess.Read, FileShare.Write);
                long endPos = stream.Length / charSize, oldPos = 0;
                long posLength;
                do
                {
                    printed = false;
                    noOfLines = 0;
                    buffer = new byte[charSize];
                    endPos = stream.Length / charSize;
                    if (endPos <= oldPos) oldPos = endPos;  	// if file's content is 
							//deleted, reset position
                    posLength = endPos - oldPos;

                    for (long pos = charSize; pos <= posLength; pos += charSize)
                    {
                        stream.Seek(-pos, SeekOrigin.End);
                        stream.Read(buffer, 0, charSize);
                        temp = encoding.GetString(buffer);
                        if (temp == newline)
                        {
                            noOfLines++;
                        }
                        if (noOfLines == noOfLinesWanted || pos == noOfCharsWanted)
                        {
                            buffer = new byte[endPos - stream.Position];
                            stream.Read(buffer, 0, buffer.Length);
                            Console.WriteLine(encoding.GetString(buffer));
                            printed = true;
                            oldPos = endPos;
                            break;
                        }
                    }
                    if (!printed)
                    {
                        buffer = new byte[endPos - oldPos];
                        stream.Seek(-1, SeekOrigin.Current);
                        stream.Read(buffer, 0, buffer.Length);
                        Console.WriteLine(encoding.GetString(buffer));
                        oldPos = endPos;
                    }
                    if (loop && endPos == stream.Length / charSize) Thread.Sleep(0);
                } while (true);
            }
            catch (ArgumentException)
            {
                printUsage();
            }
            catch (IOException)
            {
                printUsage();
                returnStatus = -5;
            }
            catch (Exception)
            {
                Console.WriteLine("Encountered some error.");
                returnStatus = -1;
            }
            finally
            {
                if (stream != null)
                    stream.Close();
            }
            
            return returnStatus;
        }
        static void printUsage()
        {
            Console.Write("Usage : ");
            Console.WriteLine("tail [OPTION]... [FILE]");
            Console.WriteLine("");
            Console.WriteLine("Print the last 10 lines of FILE to console.");
            Console.WriteLine("");
            Console.WriteLine("OPTION :");
            Console.WriteLine("");
            Console.WriteLine("-f : keep scanning the file till user aborts");
            Console.WriteLine("-n=[no. of lines] : from end to read. Default is 10.");
            Console.WriteLine("-c=[no. of chars] : from end to read. 
				Default is 800 ascii chars. ");
            Console.WriteLine("-e=[ascii/unicode] : type of encoding to use 
				while reading the file. Default is ASCII.");
            Console.WriteLine("");
        }
        static void ParseArgs(string[] args)
        {
            string[] option = null;
            if (args.Length == 0)
            {
                throw new ArgumentException();   
            }
            foreach (string arg in args)
            {
                option = arg.Split(new char[]{'='}, StringSplitOptions.None);
                switch (option[0])
                {
                    case "-f":
                        loop = true;
                        break;
                    case "-n":
                        noOfLinesWanted = int.Parse(option[1]);
                        if (noOfLinesWanted < 1 || noOfLinesWanted > 40) 
						noOfLinesWanted = 10;
                        break;
                    case "-c":
                        noOfCharsWanted = int.Parse(option[1]);
                        if (noOfCharsWanted < 1 || noOfCharsWanted > 800) 
						noOfCharsWanted = 800;
                        break;
                    case "-e":
                        encoding=Encoding.GetEncoding(option[1]);
                        break;
                    default:
                        fileName = arg;
                        break;
                }
            }
        }
        private static string fileName;
        private static int noOfLinesWanted = 10;
        private static int noOfCharsWanted = 10 * 80 * 1; //10 lines, 80 chars per line,
						   //1 ascii char 
        private static Encoding encoding = Encoding.ASCII;
        private static bool loop = true;
    }
}

待办事项

我有一些想做的事情

  1. 从文件中读取一个数据块。这样会更有效率。
  2. 如果可能的话,使用文件更改事件。

肯定存在一些测试用例会破坏这段代码。我知道,我还没有充分测试它。所以请告诉我这些场景,我希望能尝试修复它。

另外,如果你认为我应该支持某些功能,请告诉我。

更新/修复

  • 更新以处理 "-n" 和 "-c" 的无效输入

最后

请评论这篇文章并评分。:)

© . All rights reserved.