背景:

> 之前做 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 控制台程序 的 程序调用(支持输入命令得到返回字符串输出)的更多相关文章

  1. 『片段』OracleHelper (支持 多条SQL语句)

    C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...

  2. 『片段』Win32 模式窗体 消息路由

    需求背景 近来,有个需求: 和一个外部程序对接. 具体是,我这边 主程序用 Process 启动外部程序.外部程序启动后,我这边调用的窗体不允许再进行任何操作. 当外部程序关闭时,外部程序会向我这边的 ...

  3. 『片段』C# DateTime 时间相减 和 时区的关系

    本文只是基础代码片段,直接先写 结论: C# DateTime 时间相减 —— 和 时区无关,只和时间值有关. 运行结果: 测试代码: using System; using System.Colle ...

  4. 『OpenCV3』Harris角点特征_API调用及python手动实现

    一.OpenCV接口调用示意 介绍了OpenCV3中提取图像角点特征的函数: # coding=utf- import cv2 import numpy as np '''Harris算法角点特征提取 ...

  5. 『TensorFlow』专题汇总

    TensorFlow:官方文档 TensorFlow:项目地址 本篇列出文章对于全零新手不太合适,可以尝试TensorFlow入门系列博客,搭配其他资料进行学习. Keras使用tf.Session训 ...

  6. 用chrome的snippets片段功能创建页面js外挂程序,从控制台创建js小脚本

    用chrome的snippets片段功能创建页面js外挂程序,从控制台创建js小脚本 Chrome的snippets是小脚本,还可以创作并在Chrome DevTools的来源面板中执行.可以访问和从 ...

  7. C#控制台或应用程序中两个多个Main()方法的可行性方案

    大多数初级程序员或学生都认为在C#控制台或应用程序中只能有一个Main()方法.但是事实上是可以有多个Main()方法的. 在C#控制台或应用程序中,在多个类中,且每个类里最多只能存在一个Main() ...

  8. C#控制台或应用程序中两个多个Main()方法的设置

    大多数初级程序员或学生都认为在C#控制台或应用程序中只能有一个Main()方法.但是事实上是可以有多个Main()方法的. 在C#控制台或应用程序中,在多个类中,且每个类里最多只能存在一个Main() ...

  9. 【实验 1-1】编写一个简单的 TCP 服务器和 TCP 客户端程序。程序均为控制台程序窗口。

    在新建的 C++源文件中编写如下代码. 1.TCP 服务器端#include<winsock2.h> //包含头文件#include<stdio.h>#include<w ...

随机推荐

  1. 大型EMR电子病历源码三甲医院医疗信息管理系统软件网络版

    详情请点击查看 开发环境 :VS2010 + C# + ORACLE系统简介:1各种记录的书写,并可保留修改痕迹 在各种记录的书写过程中,根据系统提供的首次护理记录.一般护理记录.术前术后护理记录等模 ...

  2. css那些事(一)

    一.内边框padding和外边框margin属性缩写 内外边框有四个属性:padding-top,padding-right,padding-bottom,padding-left;margin-to ...

  3. 总结Array类型中常用的方法

    Array类型应该是 ECMAScript 中最常用的类型之一了,并且它定义的数组与其他语言有着相当大的区别.数组是数据的有序集合,我们可以通过下标对指定位置的数据进行读 写:特别的是,在 ECMAS ...

  4. java面试总结

    一.java的集合框架 HashMap.HashTable.CurrentHashMap的底层数据结构与区别? CurrentHashMap与HashTable是如何保证线程安全的? ArrayLis ...

  5. 使用XHProf分析PHP性能瓶颈(二)

    上一篇文章里,我们介绍了如何基于xhprof扩展来分析PHP性能,并记录到日志里,最后使用xhprof扩展自带的UI在web里展示出来.本篇文章将讲述2个知识点: 使用xhgui代替xhprof的默认 ...

  6. iframe跨域动态设置主窗口宽高

    Q:在A项目的a页面嵌入一个iframe,src是B项目的b页面,怎样让a页面的高度跟b页面的高度一样? A:解决跨域方案:增加一个A项目的c页面. 操作步骤: 一,a页面的iframe设置: 获取到 ...

  7. PAT1115:Counting Nodes in a BST

    1115. Counting Nodes in a BST (30) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...

  8. Kali Linux虚拟机安装完整安装过程及简单配置(视频)

    点击播放视频 附:视频中出现的两个txt文本,包含了大致的安装与配置过程: 文本1:KaliLinux虚拟机安装和初步配置 Kali Linux虚拟机安装和初步配置 大家好,今天给大家演示一下在VMw ...

  9. 使用XAMPP和DVWA在Windows7上搭建渗透测试环境

    前言: XAMPP是一个Web应用程序运行环境集成软件包,包括MySQL.PHP.PerI和Apache的环境及Apache.MySQL.FilleZilla.Mercury和Tomecat等组件.D ...

  10. 关于new date()获取服务器时间与linux系统时间不一致的解决办法 2017.12.6

    在catalina.sh  第一行添加一下脚本JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+08"