SSH.NET 从 ShellStream 检索输出

2024-01-03

我是 SSH.NET 的新手,我正在我目前正在进行的一个项目中使用它。

我必须使用 SSH.NET 运行 sudo 命令,这就是我使用 ShellStream 运行该命令并为 sudo 命令提供身份验证的原因。现在我尝试运行 sudo 命令,该命令在我通过 ssh 访问的服务器上的目录中查找文件。命令如下:

sudo -S find -name 29172

该命令应该输出一个指示文件所在位置的路径,如下所示:

./output/directory/29172

我现在遇到的问题是,当我通过 shell 流执行此操作时,我不知道如何获取输出。即使当我尝试读取 ShellStream 时,我也会得到以下结果:

var client = new SshClient(IP, username, password);

var stream = client.CreateShellStream("input", 0, 0, 0, 0, 1000000);

stream.WriteLine("sudo -S find . -name 29172");

stream.WriteLine("\"" +password+"\"");

var output = stream.ReadToEnd();

输出通常给出的是我何时使用登录服务器的描述SSH.NET然后是我向系统提供的命令:

"Last login: Mon Sep 23 15:23:35 2019 from 100.00.00.00\r\r\nserver@ubuntu-dev4:~$ sudo -S find . -name 29172\r\n[sudo] password for server: \r\n"

我不是在寻找这个输出,而是在寻找命令的实际输出,例如"./output/directory/29172"来自 ShellStream。有人知道该怎么做吗?感谢您的阅读,希望很快能收到您的来信。


我的解决方案相当冗长,但它做了一些其他必要的事情来通过 ssh 可靠地运行命令:

  • 自动响应身份验证请求
  • 捕获错误代码并引发故障

要通过 SSH 自动执行 sudo,我们可以使用Expect- 这就像 Linux 上的同名工具一样,可以让你交互响应。它会等待,直到有一些输出与模式匹配,例如密码提示。

如果您有一系列 sudo 操作,您可能会遇到不可预测的时间,直到 sudo 需要重新身份验证,因此 sudo 可能需要也可能不需要身份验证,我们无法确定。

自动化时的一个大问题是了解命令是否失败。唯一了解的方法是通过 shell 获取最后一个错误。我抛出非零。

可能需要根据您的配置自定义与 shell 提示符匹配的正则表达式。各种各样的东西都可能被注入到提示中。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Renci.SshNet;
using Renci.SshNet.Common;

namespace Example
{
    public class Log
    {
        public static void Verbose(string message) =>
            Console.WriteLine(message);

        public static void Error(string message) =>
            Console.WriteLine(message);
    }

    public static class StringExt
    {
        public static string StringBeforeLastRegEx(this string str, Regex regex)
        {
            var matches = regex.Matches(str);

            return matches.Count > 0
                ? str.Substring(0, matches.Last().Index)
                : str;

        }

        public static bool EndsWithRegEx(this string str, Regex regex)
        {
            var matches = regex.Matches(str);

            return
                matches.Count > 0 &&
                str.Length == (matches.Last().Index + matches.Last().Length);
        }

        public static string StringAfter(this string str, string substring)
        {
            var index = str.IndexOf(substring, StringComparison.Ordinal);

            return index >= 0
                ? str.Substring(index + substring.Length)
                : "";
        }

        public static string[] GetLines(this string str) =>
            Regex.Split(str, "\r\n|\r|\n");
    }

    public static class UtilExt
    {
        public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> func) 
        {
            foreach (var item in sequence)
            {
                func(item);
            }
        }
    }

    public class Ssh
    {
        SshClient sshClient;
        ShellStream shell;
        string pwd = "";
        string lastCommand = "";

        static Regex prompt = new Regex("[a-zA-Z0-9_.-]*\\@[a-zA-Z0-9_.-]*\\:\\~[#$] ", RegexOptions.Compiled);
        static Regex pwdPrompt = new Regex("password for .*\\:", RegexOptions.Compiled);
        static Regex promptOrPwd = new Regex(Ssh.prompt + "|" + Ssh.pwdPrompt);

        public void Connect(string url, int port, string user, string pwd)
        {
            Log.Verbose($"Connect Ssh: {user}@{pwd}:{port}");

            var connectionInfo =
                new ConnectionInfo(
                    url,
                    port,
                    user,
                    new PasswordAuthenticationMethod(user, pwd));

            this.pwd = pwd;
            this.sshClient = new SshClient(connectionInfo);
            this.sshClient.Connect();

            var terminalMode = new Dictionary<TerminalModes, uint>();
            terminalMode.Add(TerminalModes.ECHO, 53);

            this.shell = this.sshClient.CreateShellStream("", 0, 0, 0, 0, 4096, terminalMode);

            try
            {
                this.Expect(Ssh.prompt);
            }
            catch (Exception ex)
            {
                Log.Error("Exception - " + ex.Message);
                throw;
            }
        }

        public void Disconnect()
        {
            Log.Verbose($"Ssh Disconnect");

            this.sshClient?.Disconnect();
            this.sshClient = null;
        }

        void Write(string commandLine)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Log.Verbose("> " + commandLine);
            Console.ResetColor(); 

            this.lastCommand = commandLine;

            this.shell.WriteLine(commandLine);
        }

        string Expect(Regex expect, double timeoutSeconds = 60.0)
        {
            var result = this.shell.Expect(expect, TimeSpan.FromSeconds(timeoutSeconds));

            if (result == null)
            {
                throw new Exception($"Timeout {timeoutSeconds}s executing {this.lastCommand}");
            }

            result = result.Contains(this.lastCommand) ? result.StringAfter(this.lastCommand) : result;
            result = result.StringBeforeLastRegEx(Ssh.prompt);
            result = result.Trim();

            result.GetLines().ForEach(x => Log.Verbose(x));

            return result;
        }

        public string Execute(string commandLine, double timeoutSeconds = 30.0)
        {
            Exception exception = null;
            var result = "";
            var errorMessage = "failed";
            var errorCode = "exception";

            try
            {
                this.Write(commandLine);
                result = this.Expect(Ssh.promptOrPwd);

                if (result.EndsWithRegEx(pwdPrompt))
                {
                    this.Write(this.pwd);
                    this.Expect(Ssh.prompt);
                }

                this.Write("echo $?");
                errorCode = this.Expect(Ssh.prompt);

                if (errorCode == "0")
                {
                    return result;    
                }
                else if (result.Length > 0)
                {
                    errorMessage = result;
                }
            }
            catch (Exception ex)
            {
                exception = ex;
                errorMessage = ex.Message;
            }

            throw new Exception($"Ssh error: {errorMessage}, code: {errorCode}, command: {commandLine}", exception);
        }
    }
}

然后像这样使用它:


var client = new Ssh(IP, 22, username, password);

var output = client.Execute("sudo -S find . -name 29172");
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SSH.NET 从 ShellStream 检索输出 的相关文章

随机推荐

  • =* sql 中的运算符

    我正在打字 手指很粗 当我打字时 在 sql 窗口中 2008 SSMS 连接到 2005 服务器 它作为关键字变成蓝色 我无法弄清楚 或者谷歌 这是做什么的 我知道 但不知道 这个操作员是做什么的 是一种编写右外连接的旧方法 例如 sel
  • NodeJS 中不同路径的并行请求:长时间运行的路径 1 阻塞其他路径

    我正在尝试简单的 NodeJS 应用程序 以便我能够理解异步本质 但我的问题是 一旦我点击 home 从浏览器等待响应 同时 被击中 它等待 home先回复 再回复 要求 我担心的是 如果其中一个请求需要大量处理 那么我们不能同时请求另一个
  • 有没有办法否定 .dockerignore 中的模式?

    我试图否定 dockerignore 中的模式 Globbing 是使用 Go 的 filepath Match 规则完成的 检查后source http golang org src pkg path filepath match go
  • Javafx 8:在初始化方法中填充 TableView

    我是 JavaFX 8 的新手 我正在尝试使用初始化方法向 TableView 提供控制器中的一些数据 我看过很多关于它的话题 尝试了很多东西 但它对我不起作用 我见过 如何使用 FXML 和 JavaFX 动态填充 TableView h
  • Angular 2 - 如何显示多选下拉列表的选定选项?

  • Team Build 2012 是否与 Team Foundation Server 2010 兼容?

    连接是一种什么体验团队建设 http en wikipedia org wiki Team Foundation Server Team Build2012年至团队基础服务器 http en wikipedia org wiki Team
  • 如何计算根据条件选择的一组行中的元素数量?

    我有一个大DataFrame有许多重复值 唯一值存储在List1 我想做以下事情 选择包含列表中存在的每个值的几行 迭代选定的行并计算非 NaN 元素的数量 如果计数值大于或等于2 则将其存储到新列表中 中的每个组件List1应添加到eq
  • 当read.table中5行之后出现不同数量的列时,fill=TRUE会失败吗? [复制]

    这个问题在这里已经有答案了 假设我们有一个文件名test txt其中包含未知数量的列 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4
  • 如何从 React Redux 中的子组件进行分派?

    我的服务器有这样的代码
  • Android 多实例

    我在android中的通知中遇到问题 每当我单击通知时 我都必须再次调用相同的活动 但据我认为新的活动被调用 但前一个活动也在后端运行 因为我的代码一次又一次地运行 因为有多个实例 请帮助我如何在每次单击通知时解决或关闭多个实例 code
  • 当文档窗口在 Visual Studio 中获得焦点时发生的事件

    我在 Visual Studio 中有一个可扩展性项目 我需要使用当我在 Visual Studio 编辑器中从一个窗口更改为另一个窗口时触发的事件 我的问题是 我创建了一个显示一些图表的工具窗口 该图表依赖于一个可编辑文件 当我保存可编辑
  • Nestjs同时设置http和https服务器

    我正在尝试设置 http 和 https 服务器 咨询了官方文件 https docs nestjs com faq multiple servers nest i System Information OS Version Windows
  • 可从同一类的另一个实例访问私有字段[重复]

    这个问题在这里已经有答案了 我没有得到以下信息 我一直认为我只能从声明该字段的类访问私有字段 但是在这种情况下我可以访问它 class Session List
  • 子类化用户控制的 GridView

    我正在尝试对位于 UserControl 中的 GridView 进行子类化 因此 我希望能够在单独的页面中处理事件 基本上我的代码如下 我的带有 GridView 的 UserControl div div
  • 二分查找,从 java 到 Actionscript

    我正在尝试将以下 java 二进制搜索例程转换为 as3 我假设 compareTo 是内置的java方法 而 gt gt gt 是一种按位运算 任何熟悉 ActionScript 3 和 Java 的人都可以帮忙解决这个问题吗 packa
  • Excel 单元格值被 OLEDB 提供程序截断

    我正在使用 OleDbConnection 类从 Excel 2000 2003 工作簿检索数据 string connectionString Provider Microsoft Jet OLEDB 4 0 Data Source fi
  • 为什么垂直滚动条会自动移动?

    例如 我不明白为什么当单击 Line 9 时垂直滚动条会自动移动到最顶部位置 进一步单击不会移动滚动条 谁能解释为什么以及如何解决这个问题 我使用 Firefox 3 6 3 HTML div div
  • Fortran 中对多个矩阵进行加权和求和的最有效方法

    我正在尝试用 Fortran 编写一个函数 将多个具有不同权重的矩阵相乘 然后将它们加在一起形成一个矩阵 我已经确定这个过程是我的程序的瓶颈 这个权重将被many程序单次运行的时间 具有不同的权重 现在我正试图通过从 Matlab 切换到
  • 找不到项目文件

    我有一个现有的 TFS 项目 其结构与此类似 我在框架目录中创建了一个包含 2 个项目的解决方案 然后 我将 Cerberus 和 Cerberus Mvc 作为现有项目添加到 OnFaitQuoi 解决方案中 请注意 之前已签入 cerb
  • SSH.NET 从 ShellStream 检索输出

    我是 SSH NET 的新手 我正在我目前正在进行的一个项目中使用它 我必须使用 SSH NET 运行 sudo 命令 这就是我使用 ShellStream 运行该命令并为 sudo 命令提供身份验证的原因 现在我尝试运行 sudo 命令