Nuget
CliWrap
---> 本机命令包装 https://github.com/Tyrrrz/CliWrap
SSH.NET
---> C#版本的 SSH, SFTP
Furion.Tools.CommandLine
---> 便捷的编写命令行工具
执行本机命令
例子
var buildFolderPath = @"E:\ReactProject\build\";
// 执行压缩命令
NativeCmdUtils.ExecCmdAndListenAsync(
"E:\\Software\\7-Zip\\7z.exe", "a publish.7z .",
buildFolderPath
)
.Wait();
帮助类
using System;
using System.Text;
using System.Threading.Tasks;
using CliWrap.EventStream;
using Furion.Tools.CommandLine;
using Renci.SshNet;
using Cmd = CliWrap;
namespace JJDSPublish
{
public static class NativeCmdUtils
{
public static async Task ExecCmdAndListenAsync(string filePath, string arg, string workingDir)
{
var cmd = CliWrap.Cli.Wrap(filePath).WithArguments(arg)
.WithWorkingDirectory(workingDir);
await foreach (var cmdEvent in cmd.ListenAsync())
{
switch (cmdEvent)
{
case StartedCommandEvent started:
break;
case StandardOutputCommandEvent stdOut:
Cli.WriteLine($"{stdOut.Text}", ConsoleColor.Green);
break;
case StandardErrorCommandEvent stdErr:
Cli.WriteLine($"{stdErr.Text}", ConsoleColor.Red);
break;
case ExitedCommandEvent exited:
if (exited.ExitCode != 0)
{
throw new Exception("执行命令失败");
}
break;
}
}
}
public static void ExecCmd(string filePath, string arg, string workingDir)
{
var stdOutBuffer = new StringBuilder();
var stdErrBuffer = new StringBuilder();
var cmd = Cmd.Cli.Wrap(filePath).WithArguments(arg)
.WithWorkingDirectory(workingDir)
.WithStandardOutputPipe(Cmd.PipeTarget.ToStringBuilder(stdOutBuffer))
.WithStandardErrorPipe(Cmd.PipeTarget.ToStringBuilder(stdErrBuffer))
.ExecuteAsync().GetAwaiter().GetResult();
var stdOut = stdOutBuffer.ToString();
var stdErr = stdErrBuffer.ToString();
if (cmd.ExitCode != 0 || !string.IsNullOrWhiteSpace(stdErr) || string.IsNullOrWhiteSpace(stdOut))
{
throw new Exception("执行失败:"+stdErr);
}
Cli.Success(stdOut);
}
}
}
操作远程服务器
例子
上传文件 (下载文件类似)
var remoteInfo = "1.117.xxx.xxx:22,root,xxxxxx";
var zipFilePath = @"E:\Work\publish.7z";
var remoteFilePath = "/data/publish.7z";
RemoteServerUtils.GetSftpRemoteClient(remoteInfo)
.UploadFile(
zipFilePath,
remoteFilePath
)
.Disconnect();
执行远程命令
var remoteInfo = "1.117.xxx.xxx:22,root,xxxxxx";
var remoteClient = RemoteServerUtils.GetCmdRemoteClient(remoteInfo);
remoteClient.RunCmd("cd /data/nginx/html/ && 7z x publish.7z -o./jjds && chmod 777 -R /data/nginx/html/jjds/ && docker start nginx && rm -r -f /data/nginx/html/publish.7z");
帮助类
using System;
using System.Data;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Furion.Tools.CommandLine;
using Renci.SshNet;
namespace JJDSPublish
{
public class RemoteServerUtils
{
public static RemoteCmd GetCmdRemoteClient(string remoteInfo)
{
return new RemoteCmd(remoteInfo);
}
public static RemoteSftp GetSftpRemoteClient(string remoteInfo)
{
return new RemoteSftp(remoteInfo);
}
#region 封装
private static RemoteServerInfo GetServerInfo(string serverInfo)
{
var info = serverInfo.Split(",");
var hostInfo = info[0].Split(":");
return new RemoteServerInfo()
{
Host = hostInfo[0],
Port = int.Parse(hostInfo[1]),
UserName = info[1],
PassWord = info[2]
};
}
public class RemoteCmd
{
private readonly SshClient _sshClient;
public RemoteCmd(string remoteInfo)
{
var serveInfo = GetServerInfo(remoteInfo);
var sshClient =
new SshClient(serveInfo.Host, serveInfo.Port, serveInfo.UserName, serveInfo.PassWord);
sshClient.Connect();
if (!sshClient.IsConnected)
{
throw new Exception("sshClient 未连接");
}
this._sshClient = sshClient;
}
public void Disconnect()
{
_sshClient.Disconnect();
_sshClient.Dispose();
}
public RemoteCmd RunCmd(string cmdStr)
{
var cmd = _sshClient.RunCommand(cmdStr);
if (cmd.ExitStatus != 0)
{
throw new Exception(cmd.Error);
}
Cli.Success(cmd.Result);
return this;
}
}
public class RemoteSftp
{
private readonly SftpClient _sftpClient;
private static int usingResource = 0;
public RemoteSftp(string remoteInfo)
{
var serveInfo = GetServerInfo(remoteInfo);
var sftpClient =
new SftpClient(serveInfo.Host, serveInfo.Port, serveInfo.UserName, serveInfo.PassWord);
sftpClient.Connect();
if (!sftpClient.IsConnected)
{
throw new Exception("sftpClient 未连接");
}
_sftpClient = sftpClient;
}
public void Disconnect()
{
_sftpClient.Disconnect();
_sftpClient.Dispose();
}
public RemoteSftp UploadFile(string inputFilePath, string remoteFilePath)
{
Cli.EmptyLine();
var file = File.Open(inputFilePath, FileMode.Open);
var fileSize = HumanReadableFilesize(file.Length);
_sftpClient.UploadFile(file,
remoteFilePath, (v) =>
{
if (0 == Interlocked.Exchange(ref usingResource, 1))
{
var bfb = Math.Round((double.Parse(v + "") / file.Length) * 100, 2) + "%";
Cli.Write($"正在上传: {HumanReadableFilesize(v)}/ {fileSize} --- {bfb}", fillLine: true);
Thread.Sleep(3000);
CliExtend.ResetLine();
Interlocked.Exchange(ref usingResource, 0);
}
});
Cli.EmptyLine();
Cli.WriteLine("上传成功:" + remoteFilePath);
return this;
}
public RemoteSftp DownloadFile(string remoteFilePath, string outputFilePath)
{
Cli.EmptyLine();
using var stream = File.Open(outputFilePath, FileMode.OpenOrCreate);
_sftpClient.DownloadFile(remoteFilePath, stream,
(v) =>
{
if (0 == Interlocked.Exchange(ref usingResource, 1))
{
Cli.Write("正在下载:" + HumanReadableFilesize(v));
Thread.Sleep(3000);
CliExtend.ResetLine();
Interlocked.Exchange(ref usingResource, 0);
}
});
Cli.EmptyLine();
Cli.WriteLine("下载成功:" + outputFilePath);
return this;
}
private String HumanReadableFilesize(double size)
{
String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "PB" };
double mod = 1024.0;
int i = 0;
while (size >= mod)
{
size /= mod;
i++;
}
return Math.Round(size, 3) + units[i];
}
}
private class RemoteServerInfo
{
public string Host { get; set; }
public int Port { get; set; }
public string UserName { get; set; }
public string PassWord { get; set; }
}
#endregion
}
}
CliExtend
using System;
namespace Furion.Tools.CommandLine
{
public static class CliExtend
{
public static void WriteSplitLine(string content = "")
{
if (content != "")
{
content = " " + content + " ";
}
Cli.EmptyLine();
Cli.WriteLine($"==========================={content}===========================", ConsoleColor.Gray);
Cli.EmptyLine();
}
public static void ResetLine()
{
Console.SetCursorPosition(0, Console.CursorTop);
}
}
}