为winform增加运行shell脚本的能力

问题

在使用winform进行上位机开发时,需要运行一段shell脚本程序,该脚本原本是运行在Linux设备中的,主要用于分析文本文件,用到了cat、awk、grep、sed四个常用 操作。

解决思路

在windows下,可以使用类UNIX模拟环境(如:Cygwin、msys等)来实现在 windows之下运行linux脚本。为了减少开发量,可以利用多进程方式调用模拟环境运行脚本,然后把标准输出显示出来。但是这些环境都比较庞大,把整个类UNIX模拟环境打包到上位机里面,将会大大增加软件包的尺寸。因此,需要提取用到的工具进行打包。

实践

执行命令行

执行命令行时,可以使用同步方式,等整个脚本执行结束时再返回,并把所有的输出一并返回;也可以使用异步方式,脚本中如果有标准输出,会实时把消息通过事件传出来。这里使用异步方式。

为方便操作,这里封装了一个异步的指令执行器:

    class AsynCmdHandler
    {
        public delegate void CmdExitCallback();
        public delegate void CmdOutputDataReceiver(string data);
        public delegate void CmdErrorDataReceiver(string data);

        private Process p = new Process();
        private ProcessStartInfo si = new ProcessStartInfo();
        /// <summary>
        /// 命令行结束后返回
        /// </summary>
        public event CmdExitCallback FinishCallback;
        /// <summary>
        /// 标准输出数据接收事件
        /// </summary>
        public event CmdOutputDataReceiver OutputDataReceiver;
        /// <summary>
        /// 错误数据接收事件
        /// </summary>
        public event CmdErrorDataReceiver ErrorDataReceiver;

        /// <summary>
        /// 执行命令行指令
        /// </summary>
        /// <param name="cmd">指令内容,包括命令行参数</param>
        public void RunCmd(string cmd)
        {
            var path = Path.Combine(Environment.SystemDirectory, @"cmd.exe");
            si.FileName = path;
            if (!cmd.StartsWith(@"/"))
            {
                cmd = @"/c " + cmd;
            }
                
            si.Arguments = cmd;
            si.UseShellExecute = false;
            si.RedirectStandardOutput = true;
            si.RedirectStandardError = true;
            si.RedirectStandardInput = true;
            si.WindowStyle = ProcessWindowStyle.Hidden;

            p.StartInfo = si;
            p.EnableRaisingEvents = true; // 该参数为true时,如果程序退出会通过CmdExited返回
            p.ErrorDataReceived += CmdErrorDataReceived;
            p.OutputDataReceived += CmdOutputDataReceived;
            p.Exited += CmdExited;
            p.Start();
            p.BeginErrorReadLine();
            p.BeginOutputReadLine();
        }

        /// <summary>
        /// 写入“标准输入”数据
        /// </summary>
        /// <param name="data">数据内容</param>
        public void Write(string data)
        {
            p.StandardInput.WriteLine(data);
        }

        /// <summary>
        /// 强制退出进程,并资源
        /// </summary>
        public void Close()
        {
            if(p != null)
            {
                p.Kill();
                p.Close();
                p = null;
            }
        }

        private void CmdExited(object sender, EventArgs e)
        {
            if(p!= null)
            {
                p.Close();
            }
            
            FinishCallback?.Invoke(); // 进程执行完毕,通知应用层
        }

        private void CmdOutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            OutputDataReceiver?.Invoke(e.Data);
        }

        private void CmdErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            ErrorDataReceiver?.Invoke(e.Data);
        }
    }

运行shell脚本

shell精简

以下方法基于MSYS2环境,MSYS2是一个MSYS的独立改写版本,主要用于 shell 命令行开发环境。最新版本安装包90M,不过只需要用到其实中很少的几个文件。

其实,运行shell只需要使用sh.exe就可以了,sh.exe在msys2安装目录的bin目录下。

图1. sh.exe依赖
在这里插入图片描述
如图1所示,sh.exe还依赖了msys-2.0.dll文件,因此,最基本的shell运行环境,只需要用到这两个文件,把这两个文件拷贝出来。以下是一个最简单的helloword脚本hello.sh:

echo 'Hello, World!'

用命令行sh.exe hello.sh执行,可得到如下结果:

图2. 运行第一个脚本
在这里插入图片描述
  运行的时候出现一个警告,只需要使用sh.exe创建一个/tmp目录就可以,当然,如果知道/目录在什么位置的话,也可以直接手动创建,本次测试sh.exe是放在桌面上,/目录就是桌面,因此只需要在桌面上新那一个tmp文件夹,警告就会消除。
  为了方便管理,可以把sh.exemsys-2.0.dll放到shell文件夹中去。执行命令的时候使用如下执行:

shell/sh.exe hello.sh

对于mkdirawkgrepcat等指令,同样需要把可执行程序文件以及依赖的动态库拷贝过来。推荐使用Dependency Walker来 查看可执行文件的依赖情况。

一个简单的SHELL脚本执行器实现

Winform实现SHELL脚本执行器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值