『片段』ShellHelper 控制台程序 的 程序调用(支持输入命令得到返回字符串输出)
背景:
> 之前做 OGG 时,被 OGG的配置 恶心到了。(OGG是啥,这里就不解释了)
> 总之就是一个 控制台程序,总是得手动执行一堆命令,每次都得输入 —— 实在是打字打累了。
> 于是,搜索:Shell控制输入输出 的代码 —— 没有找到完美的。【部分网友给出的往往是:一堆命令,得到全部输出 —— 而我要的是:输入一行命令就得到对应的输出】
源码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading; namespace Temp
{
/// <summary>
/// 控制台程序Shell辅助类
/// </summary>
public class ShellHelper
{
private static List<ShellInfo> m_ListShell = null;
private static Thread m_ManageShell = null;
private static readonly object m_Locker = new object(); public static bool IsManageShellThread
{
get { return Thread.CurrentThread == m_ManageShell; }
} public static ShellInfo Start(string exePath, ShellInfoReadLine ReadLine)
{
ShellInfo shellInfo = new ShellInfo();
Process process = shellInfo.Process = new Process();
process.StartInfo.FileName = exePath;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
//process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process.OutputDataReceived += new DataReceivedEventHandler(Process_OutputDataReceived);
process.ErrorDataReceived += new DataReceivedEventHandler(Process_ErrorDataReceived); process.EnableRaisingEvents = true; // 启用Exited事件
process.Exited += new EventHandler(Process_Exited); // 注册进程结束事件 process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.StandardInput.WriteLine(); shellInfo.ReadLine = ReadLine; if (m_ListShell == null) m_ListShell = new List<ShellInfo>();
m_ListShell.Add(shellInfo);
InitShellManageThread();
return shellInfo;
} private static void InitShellManageThread()
{
if (m_ManageShell == null)
{
m_ManageShell = new Thread(ManageShell_ThreadWork);
m_ManageShell.IsBackground = true;
m_ManageShell.Start();
}
}
private static void ManageShell_ThreadWork()
{
while (m_ListShell != null && m_ListShell.Count >= )
{
try
{
lock (m_Locker)
{
foreach (ShellInfo shell in m_ListShell)
if (shell != null) shell.InvokeInputOutput();
} //线程休眠 50毫秒
AutoResetEvent eventHandle = new AutoResetEvent(false);
eventHandle.WaitOne();
eventHandle.Dispose();
}
catch (Exception ex) { Console.WriteLine("ERR: " + ex.ToString()); }
}
} private static void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
try
{
ShellInfo shell = FindShellInfo(sender as Process);
if (shell != null) shell.DataReceived(e.Data);
}
catch (Exception ex) { Console.WriteLine("ERR: " + ex.ToString()); }
}
private static void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
try
{
ShellInfo shell = FindShellInfo(sender as Process);
if (shell != null) shell.ErrorReceived(e.Data);
}
catch (Exception ex) { Console.WriteLine("ERR: " + ex.ToString()); }
}
private static void Process_Exited(object sender, EventArgs e)
{
} public static ShellInfo FindShellInfo(Process process)
{
if (process == null) return null;
ShellInfo shell = m_ListShell.Find(x => x.Process == process);
return shell;
}
} public class ShellInfo
{
private ShellState m_State = ShellState.Wait;
private DateTime m_StateTime = DateTime.MinValue; private string m_LastOutLine;
private List<ShellLine> m_ListWrite = new List<ShellLine>();
private List<string> m_ListData = new List<string>();
private List<string> m_ListError = new List<string>(); public Process Process { get; set; }
public ShellInfoReadLine ReadLine { get; set; } public ShellState State
{
get { return m_State; }
private set
{
m_State = value;
m_StateTime = DateTime.Now;
m_EventHandle.Set();
}
} public string PrevCmd { get { return string.Empty; } }
public string NextCmd { get { return string.Empty; } } public void Close()
{
try { if (Process != null && !Process.HasExited) Process.Close(); }
catch { }
} #region 输 入 输 出 处 理 private DateTime m_DataTime = DateTime.MinValue;
private DateTime m_ErrorTime = DateTime.MinValue;
private AutoResetEvent m_EventHandle = new AutoResetEvent(false);
private ManualResetEvent m_EventWaitLogoOutputHandle = new ManualResetEvent(false);
private AutoResetEvent m_EventAwaitWriteHandle = new AutoResetEvent(false);
private const int MAX_UIWAIT_MILLSECOND = * ; /// <summary>
/// 等待 Shell 的 Logo输出结束 (也就是 刚启动Shell程序时, Shell程序最先输出文本, 然后才允许用户输入, 这里等待的就是 最先输出文本的过程)
/// </summary>
public void WaitLogoOuput()
{
if (ShellHelper.IsManageShellThread) return;
m_EventWaitLogoOutputHandle.WaitOne(MAX_UIWAIT_MILLSECOND);
}
/// <summary>
/// 阻塞当前线程, 直到当前 Shell 处于 指定的状态
/// </summary>
public void WaitState(ShellState state)
{
if (ShellHelper.IsManageShellThread || m_State == ShellState.Exited) return; const int JOIN_MILL_SECOND = ; //等待毫秒数
while (m_State != ShellState.Exited && (m_State & state) != m_State)
m_EventHandle.WaitOne(JOIN_MILL_SECOND);
} /// <summary>
/// 向Shell中, 输入一段命令, 且等待命令返回 执行后的输出字符串.
/// </summary>
public string WriteLine(string cmd)
{
return WriteLine(cmd, MAX_UIWAIT_MILLSECOND);
}
/// <summary>
/// 向Shell中, 输入一段命令, 且等待命令返回 执行后的输出字符串.
/// </summary>
public string WriteLine(string cmd, int ms)
{
if (ms < ) ms = MAX_UIWAIT_MILLSECOND;
WaitLogoOuput();
WaitState(ShellState.Input | ShellState.Wait);
State = ShellState.Input; if (m_ListWrite == null) m_ListWrite = new List<ShellLine>(); cmd = (cmd ?? string.Empty).Trim();
ShellLine cmdLine = this.CurrLine = new ShellLine(m_LastOutLine, cmd);
m_ListWrite.Add(cmdLine); m_EventAwaitWriteHandle.Reset();
m_EventAwaitWriteHandle.WaitOne(ms);
return cmdLine.Result;
}
/// <summary>
/// 向Shell中, 以异步模式输入一段命令, 命令返回的输出字符串, 可以通过 ReadLine 回调捕获
/// </summary>
public void BeginWriteLine(string cmd)
{
WaitLogoOuput();
WaitState(ShellState.Input | ShellState.Wait);
State = ShellState.Input; if (m_ListWrite == null) m_ListWrite = new List<ShellLine>();
cmd = (cmd ?? string.Empty).Trim();
ShellLine cmdLine = this.CurrLine = new ShellLine(m_LastOutLine, cmd);
m_ListWrite.Add(cmdLine);
//m_EventAwaitWriteHandle.Reset();
//m_EventAwaitWriteHandle.WaitOne(MAX_UIWAIT_MILLSECOND);
} protected ShellLine CurrLine { get; set; } public void DataReceived(string str)
{
WaitState(ShellState.Output | ShellState.Wait);
State = ShellState.Output; ShellLine cmdLine = this.CurrLine;
if (cmdLine != null && !cmdLine.IsEmpty && !string.IsNullOrEmpty(str) && !cmdLine.Output)
{
Process.StandardInput.WriteLine();
string diffStr = cmdLine.GetDiffString(str);
if (!string.IsNullOrEmpty(diffStr)) m_ListData.Add(diffStr);
cmdLine.Output = true;
return;
} if (cmdLine != null) cmdLine.Output = true;
m_ListData.Add(str);
State = ShellState.Output;
}
public void ErrorReceived(string err)
{
WaitState(ShellState.OutputError | ShellState.Wait);
State = ShellState.OutputError; m_ListError.Add(err);
State = ShellState.OutputError;
} public void InvokeInputOutput()
{
if (Process == null || Process.HasExited)
{
m_EventHandle.Set();
m_EventWaitLogoOutputHandle.Set();
m_EventAwaitWriteHandle.Set();
return;
} //100 ms 没有进行 输入、输出 操作, 则管理线程开始接收 Shell的处理
const int DIFF_MILL_SECOND = ;
if (/*m_State != ShellState.Wait && */(DateTime.Now - m_StateTime).TotalMilliseconds > DIFF_MILL_SECOND)
{ ShellInfoReadLine handle = this.ReadLine;
ShellLine waitCmdLine = this.CurrLine;
string waitCmd = waitCmdLine == null ? string.Empty : waitCmdLine.Cmd; if (waitCmdLine != null || (m_ListWrite == null || m_ListWrite.Count <= ))
{
#region 正常输出
if (m_ListData != null && m_ListData.Count >= )
{
string last = m_ListData[m_ListData.Count - ];
if (!string.IsNullOrEmpty(last) && !last.Trim().EndsWith(">")) m_ListData.Add(string.Empty); string data = "\r\n" + string.Join("\r\n", m_ListData);
m_LastOutLine = last;
m_ListData.Clear();
handle(waitCmd, data);
if (waitCmdLine != null) waitCmdLine.Result = data;
this.CurrLine = null;
m_EventAwaitWriteHandle.Set();
m_EventWaitLogoOutputHandle.Set();
}
#endregion #region 异常输出
if (m_ListError != null && m_ListError.Count >= )
{
string last = m_ListError[m_ListError.Count - ];
if (!string.IsNullOrEmpty(last) && !last.Trim().EndsWith(">")) m_ListError.Add(string.Empty); string error = "\r\n" + string.Join("\r\n", m_ListError);
m_ListError.Clear();
handle(waitCmd, error);
if (waitCmdLine != null) waitCmdLine.Result = error;
this.CurrLine = null;
m_EventAwaitWriteHandle.Set();
m_EventWaitLogoOutputHandle.Set();
}
#endregion
} #region 执行输入
if (m_ListWrite != null && m_ListWrite.Count >= )
{
ShellLine cmdLine = m_ListWrite[];
this.Process.StandardInput.WriteLine(cmdLine.Cmd);
m_ListWrite.RemoveAt();
//输入命令后, 优先接收 Shell 的 错误信息
State = ShellState.OutputError;
}
else
State = ShellState.Wait;
#endregion }
} #endregion }
public class ShellLine
{
public ShellLine(string cmd)
{
this.Cmd = cmd;
}
public ShellLine(string tip, string cmd)
{
this.Tip = tip;
this.Cmd = cmd;
} public string Tip { get; set; }
public string Cmd { get; set; }
public string Result { get; set; } public bool Output { get; set; }
public bool IsEmpty
{
get { return string.IsNullOrEmpty(this.Cmd); }
}
public string Line
{
get { return Tip + Cmd; }
} public string GetDiffString(string str)
{
if (string.IsNullOrEmpty(str)) return string.Empty; string tip = this.Tip;
string line = this.Line;
if (str.StartsWith(line)) return str.Substring(line.Length);
if (str.StartsWith(tip)) return str.Substring(tip.Length);
return str;
}
} [Flags]
public enum ShellState
{
/// <summary>
/// Shell 暂时没有任何输入输出, 可能是在等待用户输入, 也可能是Shell正在处理数据
/// </summary>
Wait = ,
/// <summary>
/// 正在向 Shell 中写入命令
/// </summary>
Input = ,
/// <summary>
/// Shell 正式输出 正常信息
/// </summary>
Output = ,
/// <summary>
/// Shell 正在输出 错误信息
/// </summary>
OutputError = ,
/// <summary>
/// Shell 已经退出
/// </summary>
Exited = , } public delegate void ShellInfoReadLine(string cmd, string result); }
调用:
ShellInfo shell = ShellHelper.Start("cmd.exe", (cmd, rst) => { });
shell.WaitLogoOuput(); //先等程序把 LOGO 输出完 string aaa = shell.WriteLine("D:"); //相当于在 cmd 中输入 D:
string bbb = shell.WriteLine("dir"); //相当于在 cmd 中输入 dir
string ccc = shell.WriteLine("AAAA");
截图:
『片段』ShellHelper 控制台程序 的 程序调用(支持输入命令得到返回字符串输出)的更多相关文章
- 『片段』OracleHelper (支持 多条SQL语句)
C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...
- 『片段』Win32 模式窗体 消息路由
需求背景 近来,有个需求: 和一个外部程序对接. 具体是,我这边 主程序用 Process 启动外部程序.外部程序启动后,我这边调用的窗体不允许再进行任何操作. 当外部程序关闭时,外部程序会向我这边的 ...
- 『片段』C# DateTime 时间相减 和 时区的关系
本文只是基础代码片段,直接先写 结论: C# DateTime 时间相减 —— 和 时区无关,只和时间值有关. 运行结果: 测试代码: using System; using System.Colle ...
- 『OpenCV3』Harris角点特征_API调用及python手动实现
一.OpenCV接口调用示意 介绍了OpenCV3中提取图像角点特征的函数: # coding=utf- import cv2 import numpy as np '''Harris算法角点特征提取 ...
- 『TensorFlow』专题汇总
TensorFlow:官方文档 TensorFlow:项目地址 本篇列出文章对于全零新手不太合适,可以尝试TensorFlow入门系列博客,搭配其他资料进行学习. Keras使用tf.Session训 ...
- 用chrome的snippets片段功能创建页面js外挂程序,从控制台创建js小脚本
用chrome的snippets片段功能创建页面js外挂程序,从控制台创建js小脚本 Chrome的snippets是小脚本,还可以创作并在Chrome DevTools的来源面板中执行.可以访问和从 ...
- C#控制台或应用程序中两个多个Main()方法的可行性方案
大多数初级程序员或学生都认为在C#控制台或应用程序中只能有一个Main()方法.但是事实上是可以有多个Main()方法的. 在C#控制台或应用程序中,在多个类中,且每个类里最多只能存在一个Main() ...
- C#控制台或应用程序中两个多个Main()方法的设置
大多数初级程序员或学生都认为在C#控制台或应用程序中只能有一个Main()方法.但是事实上是可以有多个Main()方法的. 在C#控制台或应用程序中,在多个类中,且每个类里最多只能存在一个Main() ...
- 【实验 1-1】编写一个简单的 TCP 服务器和 TCP 客户端程序。程序均为控制台程序窗口。
在新建的 C++源文件中编写如下代码. 1.TCP 服务器端#include<winsock2.h> //包含头文件#include<stdio.h>#include<w ...
随机推荐
- C#文件和字节流的转换方法
1.读取文件,并转换为字节流 FileStream fs = new FileStream(filename,FileMode.Open,FileAccess.Read); byte[] infbyt ...
- EOS技术研究:合约与数据库交互
智能合约操作链数据库是很常见的应用场景.EOS提供了专门的工具来做这件事(相当于Ethereum的leveldb),专业术语叫做持久化API,本文将完整严密地介绍这个工具以及对它的使用测试. 关键字: ...
- mysql的SQL_NO_CACHE(在查询时不使用缓存)和sql_cache用法
转自:http://www.169it.com/article/5994930453423417575.html 为了测试sql语句的效率,有时候要不用缓存来查询. 使用 SELECT SQL_NO_ ...
- 基于支付系统真实场景的分布式事务解决方案效果演示: http://www.iqiyi.com/w_19rsveqlhh.html
基于支付系统真实场景的分布式事务解决方案效果演示:http://www.iqiyi.com/w_19rsveqlhh.html
- linux内核中断之看门狗
一:内核中断 linux内核中的看门狗中断跟之前的裸板的中断差不多,在编写驱动之前,需要线把内核自带的watch dog模块裁剪掉,要不然会出现错误:在Device Drivers /Watchdog ...
- SSM-Spring-06:Spring的自动注入autowire的byName和byType
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- di的注入上次讲了一些,这次主要阐述域属性的自动注入 先讲byType方式 看名字就知道是根据类型进行自动注入 ...
- Spring 数据处理框架的演变
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 定量分析的成败在很大程度上取决于采集,存储和处理数据的能力.若能及时地向业务决策者提供深刻并可靠的数据解读,大数据项目就会有更多机会取得成功 ...
- sql server 高可用故障转移(4)
二台sql服务器配置ISCSI虚拟磁盘 在上篇我们利用ISCSI Target软件在DC-ISCSCI上创建了三个ISCSI虚拟磁盘,在下面我们将为大家介绍SQL-CL01(hsr1 50)和SQL- ...
- CAP原理和BASE思想
分布式领域CAP理论,Consistency(一致性), 数据一致更新,所有数据变动都是同步的Availability(可用性), 好的响应性能Partition tolerance(分区容错性) 可 ...
- 由HashMap哈希算法引出的求余%和与运算&转换问题
1.引出问题 在前面讲解HashMap 的源码实现时,有如下几点: ①.初始容量为 1<<4,也就是24 = 16 ②.负载因子是0.75,当存入HashMap的元素占比超过整个容量的75 ...