How to pass raw string to Command in Rust? [duplicate] - rust

This question already has answers here:
How do I pass a single string with multiple arguments to std::process::Command?
(2 answers)
Closed last month.
I have a fully formed string of the command I want to run with https://doc.rust-lang.org/std/process/struct.Command.html
For example "blah -a 'test arg'", how do I pass it in without parsing it myself which would require parsing quote group into an arg?
I tried passing the rest of the args after splitting.

I found shlex which is a lexer for shell words.

Another recommended crate for this purpose is shell-words.
It offers potentially better parsing with the difference that it returns a vector of Strings, not references.
Ref: related question

You can call out to a shell to run the command. Actually the docs you've linked to have an example of doing just that.
use std::process::Command;
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(["/C", "echo hello"])
.output()
.expect("failed to execute process")
} else {
Command::new("sh")
.arg("-c")
.arg("echo hello")
.output()
.expect("failed to execute process")
};
let hello = output.stdout;
Just replace both instances of "echo hello" with the command you want to run.

Related

Python Subprocess The filename, directory name, or volume label syntax is incorrect

I am trying to download an audio dataset, I have all the audio's link stored in a csv. I read the csv and get all the links now I have to download the audio's one by one. Here's the code.
if not os.path.exists(audio_path_orig):
line = f"wget {episode_url}"
print('command:',line)
process = subprocess.Popen([(line)],shell=True)
process.wait()
for a sample, the line variable contains
wget https://stutterrockstar.files.wordpress.com/2012/08/male-episode-14-with-grant.mp3
Note that the url works and you can check for yourself, but when I try to download it using python it gives me the below error.
error: The filename, directory name, or volume label syntax is incorrect
Look at the documentation for Popen:
args should be a sequence of program arguments or else a single string or path-like object. By default, the program to execute is the first item in args if args is a sequence.
And:
Unless otherwise stated, it is recommended to pass args as a sequence.
Also:
The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle.
So use:
if not os.path.exists(audio_path_orig):
args = ["wget", f"{episode_url}"]
print('command:', " ".join(args))
result = subprocess.run(
args, capture_output=True, text=True
)
if result.returncode != 0:
print(f"wget returned error {result.returncode}")
print("Standard output:")
print(result.stdout)
print("Error output:")
print(result.stderr)

Subprocess stdout: remove unnecessary char

I´m using the subprocess module and it works fine, the only thing is that the stdout returns a value "b'" or in some cases longer text like "user config - ignore ...". Is it possible to remove this first part of the stdout without using str.substring() or similar methodes.
output = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
in the abrove example the std.out.decode() function could be used and it will be saved as < str >
decoded_output = nodes.stdout.decode()
And if some type of commands support json output(for example pvesh in proxmox) you could use the string and load it as json.
json_output = json.loads(decoded_output)

Pass JSON as command line argument to Node

I'd like to pass a JSON object as a command line argument to node. Something like this:
node file.js --data { "name": "Dave" }
What's the best way to do this or is there another more advisable way to do accomplish the same thing?
if its a small amount of data, I'd use https://www.npmjs.com/package/minimist, which is a command line argument parser for nodejs. It's not json, but you can simply pass options like
--name=Foo
or
-n Foo
I think this is better suited for a command line tool than json.
If you have a large amount of data you want to use you're better of with creating a json file and only pass the file name as command line argument, so that your program can load and parse it then.
Big objects as command line argument, most likely, aren't a good idea.
this works for me:
$ node foo.js --json-array='["zoom"]'
then in my code I have:
import * as _ from 'lodash';
const parsed = JSON.parse(cliOpts.json_array || []);
_.flattenDeep([parsed]).forEach(item => console.log(item));
I use dashdash, which I think is the best choice when it comes to command line parsing.
To do the same thing with an object, just use:
$ node foo.js --json-object='{"bar": true}'
This might be a bit overkill and not appropriate for what you're doing because it renders the JSON unreadable, but I found a robust way (as in "works on any OS") to do this was to use base64 encoding.
I wanted to pass around lots of options via JSON between parts of my program (a master node routine calling a bunch of small slave node routines). My JSON was quite big, with annoying characters like quotes and backslashes so it sounded painful to sanitize that (particularly in a multi-OS context).
In the end, my code (TypeScript) looks like this:
in the calling program:
const buffer: Buffer = new Buffer(JSON.stringify(myJson));
const command: string = 'node slave.js --json "' + buffer.toString('base64') + '" --b64';
const slicing: child_process.ChildProcess = child_process.exec(command, ...)
in the receiving program:
let inputJson: string;
if (commander.json) {
inputJson = commander.json;
if (commander.b64) {
inputJson = new Buffer(inputJson, 'base64').toString('ascii');
}
}
(that --b64 flag allows me to still choose between manually entering a normal JSON, or use the base64 version, also I'm using commander just for convenience)

Terminating process.stdin in node.js

The program below simply reads a string and outputs it. When I run this on cmd, the program doesn't print out the string. It keeps reading inputs until I terminate with Ctrl+C. How do I tell the program when my input string is over, so it can print the output?
var concat=require('concat-stream');
var str=[];
process.stdin.pipe(concat(function(buff){
console.log(buff.toString());
}));
concat-stream is waiting to receive a finish event. In your example that will happen when you close stdin. If you’re running this in a shell you can close stdin by pressing Ctrl+D. If you’re piping something to your process, make sure it closes its stdout when it’s done.
If you’re trying to make your script interactive in the shell, try split:
process.stdin
.pipe(require('split')())
.on('data', function (line) {
console.log('got “%s”', line);
});
Obviously the answer by Todd Yandell is the right one, and I have already upvoted it, but I wanted to add that besides split, you may also consider the use of through which creates a sort of transformer and it would also work in a interactive way, since it is not an aggregation pipe.
Like this example in which everything you write in the standard input gets uppercased in standard output interactively:
var through = require('through');
function write(buffer){
var text = buffer.toString();
this.queue(text.toUpperCase());
}
function end(){
this.queue(null);
}
var transform = through(write, end);
process.stdin.pipe(transform).pipe(process.stdout);
You may even combine it with split by doing:
process.stdin
.pipe(split())
.pipe(transform)
.pipe(process.stdout);

Calling groovy method from script

I'm writing a program in groovy, I want it to run a script with dynamically changing variables. The script is supposed to execute a method with its variables. This method is located in the program that runs the script.
The script would contain something like this:
// getAttribute is a method in my main program, the program which also runs the script
value = getValue("java.lang:type=OperatingSystem", "FreePhysicalMemorySize")
// do something with the value
if (value > 9000) { output = "ERROR" }
// the same thing maybe a few more times
value = getValue(...
I want to keep it as simple as possible.
I was working with these examples: http://groovy.codehaus.org/Embedding+Groovy
I already tried to use the GroovyScriptEngine, but it seems like the script only accepts Strings as input values. It would be great if I could hand over method pointers.
Just trying to hand over an integer like this:
def roots = 'PATH\\script.groovy'
def groscreng = new GroovyScriptEngine(roots)
def binding = new Binding()
def input = 42
binding.setVariable("input", input)
groscreng.run("script.groovy", binding)
println binding.getVariable("output")
With this script.groovy
output = ${input}
results in this error:
Caught: groovy.lang.MissingMethodException: No signature of method: script.$() is applicable for argument types: (script$_run_closure1) values: [script$_run_closure1#fcf50]
Possible solutions: is(java.lang.Object), run(), run(), any(), any(groovy.lang.Closure), use([Ljava.lang.Object;)
Receiving the value in the script as a String like "${input}" works just fine.
I know it has to work somehow and I would appreciate any help or other suggestions!
output = ${input}
isn't valid groovy
Try:
output = input

Resources