Plink Returning Unwanted Characters via C#

Plink returning unwanted characters via C#

Those are ANSI escape codes.

While using the --color=never may help, the root problem is that, the way you run Plink (feeding the command using a standard input), you make it use an interactive session. Most systems nowadays are configured to use a fancy formatting for directory listing (and other stuff) for interactive sessions to be more human friendly.

While in your case, no human is involved, so you should use a non-interactive session.

There are two ways to do that:

  • Either provide the command on Plink command-line (instead of feeding it using a standard input), like:

    plink.exe -ssh username@example.com -pw password "ls -l ... | grep ... | sort"

    This syntax implicitly forces a non-interactive mode.

  • Or use the -T switch to explicitly force the non-interactive mode:

    plink.exe -ssh username@example.com -pw password -T

    This syntax allows you to keep feeding the command using the standard input.

Using the non-interactive session (on a properly configured system) you avoid all the troubles of the interactive sessions (not just the --color). So it's a way more generic solution.


If this does not work for you, it must be because your server is misconfigured and aliases the ls to use the --color even for non-interactive sessions.

If you cannot fix the server, I suggest you use both non-interactive session (to avoid other possible troubles) and the --color=never switch. Possibly better solution than using the --color=never is using the unalias ls.

Piping input into Plink in Windows batch file adds extra line feeds

When you are executing plink without any command on its command-line, plink starts an interactive terminal session. That has lot of unwanted side effects. Including those that you face.

Add -T switch to plink command line to avoid that:

(
...
) | plink -T -ssh 192.168.1.20 -l root -pw ***

Another option is doing everything on the server-side and specifying full command on the plink commandline (in this case, you will also need to add the -batch switch):

plink -ssh 192.168.1.20 -l root -pw *** -batch "cd /usr/bin/core/test && rm STS_*.txt && rm STS_T1_Test%TestNum%.txt && (echo Y | ./STSTest --T 2 --i %TestNum%)"

Or some combination of both these methods.


Similar questions:

  • Windows batch scripting: SSH with Plink showing strange sequences in output
  • Executing command using Plink does not work, but does in PuTTY
  • Script via Plink in .bat behaves differently

Process.Start capturing standard output characters

I was looking for an answer forever, and right after asking the question here I found my solution.

Btw, thanks DarkSquirrel, you hit the nail on the head.

Here is my solution:

_processInfoTest = new ProcessStartInfo();
_processInfoTest.FileName = serviceSettings.PlinkExecutable;
_processInfoTest.Arguments = GetPlinkArguments(serviceSettings);
_processInfoTest.RedirectStandardOutput = true;
_processInfoTest.RedirectStandardError = true;
_processInfoTest.RedirectStandardInput = true;
_processInfoTest.UseShellExecute = false;
_processInfoTest.CreateNoWindow = true;

WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(_processInfoTest.Arguments, DateTime.Now));

_processTest = new Process();
_processTest.StartInfo = _processInfoTest;
_processTest.Start();

Task.Factory.StartNew(() =>
{
ProcessOutputCharacters(_processTest.StandardError);
});

Task.Factory.StartNew(() =>
{
ProcessOutputCharacters(_processTest.StandardOutput);
});

And my methods:

private static void ProcessOutputCharacters(StreamReader streamReader)
{
int outputCharInt;
char outputChar;
string line = string.Empty;

while (-1 != (outputCharInt = streamReader.Read()))
{
outputChar = (char)outputCharInt;
if (outputChar == '\n' || outputChar == '\r')
{
if (line != string.Empty)
{
ProcessLine("Output: " + line);
}

line = string.Empty;
}
else
{
line += outputChar;

if (line.Contains("login as:"))
{
_processTest.StandardInput.Write("myusername");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}

if (line.Contains("'s password:"))
{
_processTest.StandardInput.Write("mypassword");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
}
}
}

private static void ProcessLine(string line)
{
if (line != null)
{
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(line, DateTime.Now));

if (line.Contains("If you do not trust this host, press Return to abandon the"))
{
_processTest.StandardInput.Write("y");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}

if (line.Contains("Access granted"))
{
if (!_processTest.HasExited)
_processTest.Kill();

WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.Success));
}
else if (line.Contains("Access denied") || line.Contains("Password authentication failed"))
{
if (!_processTest.HasExited)
_processTest.Kill();

WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidUserOrPass));
}
else if (line.Contains("Host does not exist"))
{
if (!_processTest.HasExited)
_processTest.Kill();

WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidHostname));
}
else if (line.Contains("Connection timed out"))
{
if (!_processTest.HasExited)
_processTest.Kill();

WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.TimedOut));
}
}

}

My only problem now is when I send the username or password (through StandardInput), it's only sending the first 5 characters. I'll do some research on the issue and post that as a separate question if I need to.

Thanks!

Redirect a process's output to both a file and the console

Could use something like that

using System;
using System.Diagnostics;

namespace InteractWithConsoleApp
{
class Program
{
static void Main(string[] args)
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = @"C:\Windows\System32\cmd.exe";
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardInput = true;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = true;

Process cmdProcess = new Process();
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.ErrorDataReceived += cmd_Error;
cmdProcess.OutputDataReceived += cmd_DataReceived;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();

cmdProcess.StandardInput.WriteLine("ping google.com.ua"); //Execute ping google.com.ua
cmdProcess.StandardInput.WriteLine("exit"); //Execute exit.

cmdProcess.WaitForExit();
}

static void cmd_DataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output from other process");
Console.WriteLine(e.Data);
}

static void cmd_Error(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Error from other process");
Console.WriteLine(e.Data);
}
}
}

process not outputting it's last row C#

BeginOutputReadLine() waits for a newline character or the end of the stream. Unfortunately there is no BeginOutputRead() method that provides the behavior you desire. You do have access to process.StandardOutput though (a StreamReader), on which Read() operations do return whenever data is available.

Currently a line in a console application is "finished" as soon as it is terminated by a newline character. Since for this last line no newline character was outputted yet, you'd have to somehow determine whether it has finished or not. Consider the following example:

Console.Write("This is the ");
Console.Write(" plink output ");
Console.Write(" that I'm trying to read");
LongRunningActivity();
Console.Write(".");
Console.WriteLine();

You could receive these segments of data separately. When is the line finished? Before LongRunningActivity()?

So when attempting to read data before a newline character, you'll have to think of some rules to determine whether the message has completed.

Example of performing this task in a separate thread:

...
process.Start();
Task.Factory.StartNew(new Action<object>(ReadFromStreamReader), process.StandardOutput);

void ReadFromStreamReader(object state)
{
StreamReader reader = state as StreamReader;
char[] buffer = new char[1024];
int chars;
while ((chars = reader.Read(buffer, 0, buffer.Length)) > 0)
{
string data = new string(buffer, 0, chars);
OnDataReceived(data);
}

// You arrive here when process is terminated.
}

void OnDataReceived(string data)
{
// Process the data here. It might contain only a chunk of a full line
// remember to use Invoke() if you want to update something in your form GUI
}


Related Topics



Leave a reply



Submit