如何在C#(Mono环境)中实现tail -f功能及运行对应Bash脚本
在Mono(C#)中实现tail -f功能并处理SFTP日志事件
我来分享两种适配Mono环境的解决方案,既能实现tail -f实时跟踪日志的效果,也能满足你处理包含sftp-server行的需求:
方案一:纯C#实现tail -f逻辑
如果不想依赖Bash脚本,完全用C#代码模拟tail -f的行为是可行的。核心思路是持续监控日志文件的大小变化,当文件新增内容时读取并处理。这种方式更可控,也更贴合.NET/Mono的生态。
以下是完整的实现代码:
using System; using System.IO; using System.Text; class TailAuthLog { static void Main(string[] args) { string logPath = "/var/log/auth.log"; FileInfo logFile = new FileInfo(logPath); long lastFileSize = 0; Console.WriteLine($"开始跟踪日志: {logPath}"); Console.WriteLine("按Ctrl+C停止..."); try { while (true) { logFile.Refresh(); // 检查文件是否变大(有新内容写入) if (logFile.Length > lastFileSize) { using (FileStream stream = new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { // 定位到上次读取的位置 stream.Seek(lastFileSize, SeekOrigin.Begin); using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { string line; // 读取所有新增的行 while ((line = reader.ReadLine()) != null) { Console.WriteLine($"新日志行: {line}"); // 检查是否包含sftp-server if (line.Contains("sftp-server")) { // 在这里添加你的处理逻辑 Console.WriteLine("=== 检测到SFTP服务器日志,开始处理 ==="); // 示例:打印该行详情 Console.WriteLine($"处理内容: {line}"); // 可以在这里调用其他业务方法、写入数据库等 } } } } // 更新最后读取的文件大小 lastFileSize = logFile.Length; } // 每隔1秒检查一次,避免过度占用CPU System.Threading.Thread.Sleep(1000); } } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } }
代码说明:
- 使用
FileInfo监控文件大小变化,替代轮询整个文件的低效方式 - 通过
FileStream的Seek方法定位到上次读取的位置,只读取新增内容 - 加入
FileShare.ReadWrite确保日志文件被系统写入时,我们仍能读取 - 用
Thread.Sleep(1000)降低CPU占用,你可以根据需求调整间隔 - 当检测到包含
sftp-server的行时,直接在代码块中添加你的业务逻辑
编译运行(Mono环境):
mcs TailAuthLog.cs sudo mono TailAuthLog.exe # 需要sudo权限读取auth.log
方案二:在Mono中调用现有Bash脚本
如果你想保留已有的Bash脚本逻辑,直接在C#中启动Bash进程执行脚本也是一种简单的方案。这种方式不需要重写代码,适合快速迁移。
第一步:确保你的Bash脚本可执行
先给脚本添加执行权限:
chmod +x your_script.sh
第二步:C#调用脚本的代码实现
using System; using System.Diagnostics; class RunBashScript { static void Main(string[] args) { string scriptPath = "/path/to/your_script.sh"; // 替换为你的脚本路径 ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "/bin/bash", Arguments = scriptPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = false, // 设为true可以隐藏控制台窗口 Verb = "runas" // 用于提权,部分环境可能需要 }; using (Process process = new Process()) { process.StartInfo = startInfo; // 绑定输出和错误事件,实时获取脚本输出 process.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { Console.WriteLine($"脚本输出: {e.Data}"); } }; process.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { Console.WriteLine($"脚本错误: {e.Data}"); } }; try { process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); // 等待脚本结束(因为tail -f是持续运行的,所以这里会一直阻塞) } catch (Exception ex) { Console.WriteLine($"启动脚本失败: {ex.Message}"); } } } }
注意事项:
- 读取
/var/log/auth.log需要管理员权限,所以运行Mono程序时需要加上sudo - 如果你的Bash脚本中需要处理逻辑,直接在原脚本的
# ... 对该行进行处理位置添加即可,C#代码会实时捕获脚本的输出 - 如果不需要捕获脚本输出,可以去掉
RedirectStandardOutput和对应的事件绑定
编译运行:
mcs RunBashScript.cs sudo mono RunBashScript.exe
两种方案各有优劣:纯C#方案更灵活,适合后续扩展.NET生态的功能;调用Bash脚本方案更快捷,适合复用现有逻辑。你可以根据自己的需求选择~
内容的提问来源于stack exchange,提问作者Tono Nam
相关产品推荐
相关产品推荐

