How to deal with the error stream when using ShellStream

Feb 2, 2015 at 5:20 AM
When I use ShellStream to get the output like putty or plink,i have some questions.
First,I can get the stdout correctly by using following codes.
            ShellStream shells = client.CreateShellStream("test", 80, 24, 800, 600, 1024);
            string input = "ping 127.0.0.1 -c 10";
            shells.WriteLine(input);
            Regex regex = new Regex(@"\[.*@.*\][\$|\#]");
            
            string output = String.Empty;
            var match = regex.Match(output);
            while (shells.DataAvailable || !match.Success)
            {
                if (shells.CanRead)
                    output = shells.ReadLine();
                match = regex.Match(output);
                if (!output.StartsWith("Last login") && !(output.EndsWith(input) && i < 4))
                    Console.WriteLine(output);
                Thread.Sleep(200);
            }
and then ,here is the output
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.016 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.024 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.019 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.021 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.022 ms
64 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.026 ms
64 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.026 ms
64 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.021 ms

--- 127.0.0.1 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9000ms
rtt min/avg/max/mdev = 0.016/0.021/0.026/0.005 ms
but when i change the input like this
string input = "cd /home/t";
and ,in putty,the output is like this
-bash: cd : /home/t : No such file or directory
but,using my codes, there is no output.so, my question is how to get the error stream.
Feb 2, 2015 at 5:52 AM
What is this for?
i < 4
Error output (STDERR) is included in the shell stream.
So your code looks OK, beside the i < 4 test.
Feb 2, 2015 at 8:52 AM
da_rinkes wrote:
What is this for?
i < 4
Error output (STDERR) is included in the shell stream.
So your code looks OK, beside the i < 4 test.
thanks for reply
using the following example ,i try to explain why i use “i<4”
for((i=0;i<10;i++));
do 
    echo hello;
    echo $0;
done
my input is "./hello.sh"
without i<4 ,we will see this and it is not correct.
hello
hello
hello
hello
hello
hello
hello
hello
hello
and i can not get stderr when i remove "i<4"
Feb 2, 2015 at 11:13 AM
            client.Connect();

            var shell = client.CreateShellStream("test", 80, 24, 800, 600, 1024);
            var input = "cd /nonexistant";
            shell.DataReceived += (sender, args) => Console.Write(client.ConnectionInfo.Encoding.GetString(args.Data));
            shell.Expect(new Regex(@"\$ $")); // CMD prompt
            shell.WriteLine(input);            
=>
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-44-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Mon Feb  2 13:07:03 CET 2015

Last login: Mon Feb  2 13:07:03 2015 from xxx.xxx.xxx.xxx
~$ cd /nonexistant
-bash: cd: /nonexistant: No such file or directory
~$
Maybe your Regex is wrong or your Thread.Sleep(200) (which is basically just guessing by you)?

Why don't you use RunCommand instead of guessing and trying to find the command prompt:
            var cmd = client.RunCommand("cd /nonexistant");
            Console.WriteLine("STDERR: " + cmd.Error);
            Console.WriteLine("ERRORCODE: " + cmd.ExitStatus);
            Console.WriteLine("STDOUT: " + cmd.Result);
Marked as answer by Xiyangyang on 2/3/2015 at 1:12 AM
Feb 2, 2015 at 11:57 AM
thanks, i will try your codes .
the reason why not using RunCommand it that when i test running "nohup" command or a script containing "nohup" command and the command is finished,the program is disappeared.so i need a shell.
about "Thread.Sleep(200)".when i remove the stop,the output sometimes runs wrong. so i try to find some examples in the discussion area, and then i find some codes like this.
Feb 2, 2015 at 12:12 PM
Edited Feb 2, 2015 at 12:12 PM
Yeah... Don't use Thread.Sleep() here. This is just guessing and makes your code unreliable.
Example: Replace your "ping -c 10 127.0.0.1" with a "ping -c 10 google.com" and it will fail. Since it takes more than 200 ms for a ping reply.
Feb 2, 2015 at 12:20 PM
On second glance i found a bug in your code:
                if (shells.CanRead)
                    output = shells.ReadLine();
                match = regex.Match(output);
match.Success will never become true. ReadLine() blocks till a newline appears.
But the shell-prompt "user@host:~$" doesn't have one
=> endless blocking of Readline() and your loop would never exit.
Feb 3, 2015 at 1:01 AM
thanks for all reply
i have done the work.
here is my code.
        string input = String.Empty;
        string output = String.Empty;
        bool isFirst = true;
 
       private void SSHConnect()
        {
            client = new SshClient("172.19.118.141", "trade", "trade");
            client.ConnectionInfo.Encoding = ASCIIEncoding.Default;
            client.Connect();
            ShellStream shells = client.CreateShellStream("test", 80, 24, 800, 600, 1024);
            input = "ping 127.0.0.1 -c 5";
            shells.DataReceived += new EventHandler<Renci.SshNet.Common.ShellDataEventArgs>(shells_DataReceived); 
            shells.Expect(new Regex(@"\$ $")); 
            shells.WriteLine(input);
            shells.Expect(new Regex(@"\$ $")); 
            client.Disconnect();
        }

        void shells_DataReceived(object sender, Renci.SshNet.Common.ShellDataEventArgs e)
        {
            string newline = (client.ConnectionInfo.Encoding.GetString(e.Data));
            if (newline.Contains('\n'))
            {
                Regex regex = new Regex(@"\[.*@.*\][\$|\#]");
                string[] str = newline.Split('\n');
                for (int i = 0; i < str.Length; i++)
                {
                    output += str[i];
                    var match = regex.Match(output);
                    if (output.StartsWith("Last login") || match.Success || output == String.Empty)
                    { output = String.Empty; continue; }
                    if (isFirst && output == input)
                    { output = String.Empty; isFirst = false; continue; }
                    Console.WriteLine(output);
                    output = String.Empty;
                }
            }
            else
                output += newline;
        }