How to print ssh output in rust - rust

I am making an application a part of which it ssh into a server and runs some commands.
let mut s= session.channel_new().unwrap();
s.open_session().unwrap();
s.request_exec(input.as_ref()).unwrap();
s.send_eof().unwrap();
//standard output
let mut buf=Vec::new();
s.stdout().read_to_end(&mut buf).unwrap();
println!("{}", String::from_utf8_lossy(&buf));
//error output
let mut buf=Vec::new();
s.stderr().read_to_end(&mut buf).unwrap();
println!("{}", String::from_utf8_lossy(&buf));
this is what I have so far, but the problem am facing is that it returns terminal output in two different set, standard and error. by printing it like this I am losing the order of output from the ssh session. lets say i do sudo apt update && sudo apt upgrade and if this has any errors, it'll first print all the stuff that went well and then the error. How can I fix to have a real time output from the ssh session like the one you get from a terminal?
using https://docs.rs/ssh/latest/ssh/ for ssh

Related

Why console.log in a loop stops printing when piping to another command?

I'm using fake-words module (npm install fake-words) with the following simple code:
#!/usr/bin/env node
const fake = require("fake-words");
while (true) {
console.log(fake.sentence());
}
When I run ./genwords.js, everything works as expected.
However when I pipe into external program (on Ubuntu shell), the generation of words stops after a second.
$ ./genwords.js | cat
...
(output generation stops after a second)
$ ./genwords.js | tee
...
(stuck as well)
$ ./genwords.js | pv -l
...
4.64k 0:00:13 [0.00 /s]
Same happening when assigning a value to variable to avoid any caching (as precaution after reading this post, probably not relevant to Node.js):
while (true) {
words = fake.sentence();
console.log(words);
}
What I'm doing wrong?
I'm using Node v16 on Ubuntu:
$ node --version
v16.13.1
The behavior of console.log() in code (such as the while loop in your example) that never relinquishes control to the event loop (especially when piped to another process) in Node.js is a longstanding...uh...quirk. It's a lot harder to fix than you might think. Here's the relevant issue in the tracker: https://github.com/nodejs/node/issues/6379
Or more specifically on the whole handling-when-piped-to-another-process issue: https://github.com/nodejs/node/issues/1741
You can work around the issue by restructuring the code to relinquish control to the event loop. Here is one possibility.
#!/usr/bin/env node
const fake = require("fake-words");
function getWords() {
console.log(fake.sentence());
setImmediate(getWords);
}
getWords()

Running multiple shell commands using std::process::Command

I am trying to insert multiple commands using std::process::Command on Rust instead of running a
bash script. I have read the concept of running multiple commands using the example cited here, from the execute crate:
https://docs.rs/execute/latest/execute/#execute-multiple-commands-and-pipe-them-together.
use std::process::{Command, Stdio};
use execute::Execute;
let mut command1 = Command::new("echo");
command1.arg("HELLO WORLD");
let mut command2 = Command::new("cut");
command2.arg("-d").arg(" ").arg("-f").arg("1");
let mut command3 = Command::new("tr");
command3.arg("A-Z").arg("a-z");
command3.stdout(Stdio::piped());
let output = command1.execute_multiple_output(&mut [&mut command2, &mut command3]).unwrap();
assert_eq!(b"hello\n", output.stdout.as_slice());
The problem is these commands are being written in a scattered format. I would like to know if there are better ways to add subcommands directly as arguments under Command::new() instead of having to segregate them as command1, command2 etc.

What is the Rust equivalent of the system function in C++? [duplicate]

Is there a way to invoke a system command, like ls or fuser in Rust? How about capturing its output?
std::process::Command allows for that.
There are multiple ways to spawn a child process and execute an arbitrary command on the machine:
spawn — runs the program and returns a value with details
output — runs the program and returns the output
status — runs the program and returns the exit code
One simple example from the docs:
use std::process::Command;
Command::new("ls")
.arg("-l")
.arg("-a")
.spawn()
.expect("ls command failed to start");
a very clear example from the docs:
use std::process::Command;
let output = Command::new("/bin/cat")
.arg("file.txt")
.output()
.expect("failed to execute process");
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
It is indeed possible! The relevant module is std::run.
let mut options = std::run::ProcessOptions::new();
let process = std::run::Process::new("ls", &[your, arguments], options);
ProcessOptions’ standard file descriptors default to None (create a new pipe), so you can just use process.output() (for example) to read from its output.
If you want to run the command and get all its output after it’s done, there’s wait_with_output for that.
Process::new, as of yesterday, returns an Option<Process> instead of a Process, by the way.

Create child process that duplicates stderr of main process

Is there a stable way I can create a child process that hangs out in the background and inherits stderr, in and out? From what I see, creating a child requires me to launch a separate program. Instead I want to create a child process that lasts as long as the main process, and only serves to allow me to duplicate stderr so I can read from it.
Here's an example of creating a process inside the link
use std::process::Command;
let output = Command::new("sh")
.arg("-c")
.arg("echo hello")
.output()
.unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
let hello = output.stdout;
what I'd like to do
use std::process::Command;
let leech = Command::new() // create process that hangs out in the background and inherits stderr, stdin and stdout from main process
// ....
// panic occurs somewhere in the program
if thread::panicking {
output = leech.output().stderr();
}
// screen clears
// print stderr of output
I need to create a leech of sorts because panics being displayed to the main screen are flushed due to terminal graphics. The library will clear the screen which in the process clears away panic messages, If I was able to duplicate stderr and somehow read it, I could reprint the panic message after the terminal restored the pre-program-running state.
I believe this is easier to do with a wrapper program, instead of launching something from the rust program itself. Here's an example of how to do it using shell script:
#!/bin/bash
# Redirection magic from http://stackoverflow.com/a/6317938/667984
{ errors=$(./my_rust_program 2>&1 1>&$original_out); } {original_out}>&1
if [[ $? -ne 0 ]]; then
echo
echo "--terminal reset shenanigans--"
echo
echo "$errors" >&2
fi
When used with this rust program:
fn main() {
println!("Normal program output");
panic!("oops");
}
It prints:
Normal program output
--terminal reset shenanigans--
thread '<main>' panicked at 'oops', my_rust_program.rs:3
I believe you can create one in stable rust as well, but since you mention sh in your question I assume you are in a unix environment anyway, and the shell script version should be simpler.

How do I invoke a system command and capture its output?

Is there a way to invoke a system command, like ls or fuser in Rust? How about capturing its output?
std::process::Command allows for that.
There are multiple ways to spawn a child process and execute an arbitrary command on the machine:
spawn — runs the program and returns a value with details
output — runs the program and returns the output
status — runs the program and returns the exit code
One simple example from the docs:
use std::process::Command;
Command::new("ls")
.arg("-l")
.arg("-a")
.spawn()
.expect("ls command failed to start");
a very clear example from the docs:
use std::process::Command;
let output = Command::new("/bin/cat")
.arg("file.txt")
.output()
.expect("failed to execute process");
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
It is indeed possible! The relevant module is std::run.
let mut options = std::run::ProcessOptions::new();
let process = std::run::Process::new("ls", &[your, arguments], options);
ProcessOptions’ standard file descriptors default to None (create a new pipe), so you can just use process.output() (for example) to read from its output.
If you want to run the command and get all its output after it’s done, there’s wait_with_output for that.
Process::new, as of yesterday, returns an Option<Process> instead of a Process, by the way.

Resources