How to provide multiple line help message with Clap? - rust

Is there a way for us to have line breaks in clap's help message?
I tried multiple line comments, also tried inserting \n in the mix. But neither works.
#[derive(Parser, Debug)]
#[clap(author, version, about)]
struct Args {
/// The input filename.\n
/// Hello world!
#[clap(short, long, value_parser)]
input: String,
}
Output:
USAGE:
ascii_tree --input <INPUT>
OPTIONS:
-h, --help Print help information
-i, --input <INPUT> The input filename.\n Hello world!
-V, --version Print version information
Is there away to achieve the following?
USAGE:
ascii_tree --input <INPUT>
OPTIONS:
-h, --help Print help information
-i, --input <INPUT> The input filename.
Hello world!
-V, --version Print version information

I know of two options. Make the second line only contain ///, or use verbatim_doc_comment:
#[derive(Parser, Debug)]
struct Args {
/// Name of the person to greet
///
/// Has a multi-line help.
#[clap(short, long)]
name: String,
/// Number of times to greet
/// Use the verbatim arg
#[clap(short, long, verbatim_doc_comment)]
count: u8,
}
Playground

You can add the verbatim_doc_comment option:
#[derive(Parser, Debug)]
#[clap(author, version, about)]
struct Args {
/// The input filename.
/// Hello world!
#[clap(short, long, value_parser, verbatim_doc_comment)]
input: String,
}
USAGE:
ascii_tree --input <INPUT>
OPTIONS:
-h, --help Print help information
-i, --input <INPUT> The input filename.
Hello world!
-V, --version Print version information
It seems without it, Rust only recognizes paragraph breaks via double-newline.

Related

How to pipe string into a Rust app parsing args with Clap?

I'm Brand new to Rust and I have been writing some practice apps. I am trying to accept command-line arguments using Clap. The code below takes a string and a number and prints them back out, as such:
$ cargo run "this is a test" -n11
this is a test
11
This works fine, but I want to be able to pipe input in place of the string like this:
$ echo "this is a test" | cargo run -- -n11
this is a test
11
Trying this yields:
error: The following required arguments were not provided:
<INPUT>
USAGE:
clap_sample --num <num> <INPUT>
For more information try --help
I can get around this using xargs like this:
$ echo "this is a test" | xargs -d '\n' cargo run -- -n11
Is there a better way to do this so that I can accept piped in strings while still using the -n option? Thanks in advance.
use clap::{Arg, App};
fn main() {
let matches = App::new("Clap Sample")
.arg(Arg::new("INPUT")
.required(true)
.index(1))
.arg(Arg::new("num")
.short('n')
.long("num")
.takes_value(true))
.get_matches();
let usr_string = matches.value_of("INPUT").unwrap();
let key: u8 = matches.value_of("num").unwrap()
.parse()
.expect("NaN :(");
println!("{}", usr_string);
println!("{}", key);
}
Bonus question:
If I pipe a string in with xargs, I can have newlines in the string (with delimiter set to \0) and they are reflected in the output. If I pass it directly without echo and xargs, a literal '\n' shows in the output. Is there a way to have the newline represented when run directly?
Your code is checking the command line for arguments, it is not reading standard input. To use xargs get move the input from the pipe to the command line is a good way of doing it.
echo -n "this is a test" | xargs cargo run -- -n11
The other option you have is to change your program so it reads from stdin if no user_string argument was given. Here is a good starting point to read stdin https://doc.rust-lang.org/std/io/struct.Stdin.html
You should also replace the unwrap() here:
let key: u8 = matches.value_of("num").unwrap()
with a check if the argument was given as it was not .required(true) for instance with a
if let Some(key) = matches.value_of("num")
or maybe with a unwrap_or("0")

Why Linux shell script read to array command gives more elements than necessary?

Why following Linux shell command for given input (a-b [c=d.e] <f g>) gives extra element (3:'')?
Command:
echo "a-b [c=d.e] <f g>" | while IFS=" []<>=" read -a arr; do for ((i=0;i<${#arr[#]};i++)) do echo "${i}:'${arr[${i}]}'"; done; done
Expected output:
0:'a-b'
1:'c'
2:'d.e'
3:'f'
4:'g'
Actual output:
0:'a-b'
1:'c'
2:'d.e'
3:''
4:'f'
5:'g'
With your shown samples only(in case you are ok with awk). To get your expected output, you could do this in a single awk itself, please try following once. Simple explanation: would be, creating different field separators as per shown samples/need of OP and then traversing through all fields of current line(s) and printing only those which are required.
echo "a-b [c=d.e] <f g>" |
awk -v s1="\'" -F'[ \\[=\\]><]' '{for(i=1;i<=NF;i++){if($i){print count++":"s1 $i s1}}}'
With shown samples, output will be as follows.
0:a-b
1:c
2:d.e
3:f
4:g

Is there a Go function that works like linux cut?

This is probably a very basic question, but I have not been able to find an answer after reviewing the strings package docs.
Basically, all I want to do is the equivalent of:
echo "hello world" | cut -d" " -f2
echo "hello world" | cut -d" " -f2
This splits the string "hello world" using spaces as delimeters, and selects only the 2nd part (1-indexed).
In Go for spitting there is strings.Split() which returns a slice, which you can index or slice however you like.
s := "hello world"
fmt.Println(strings.Split(s, " ")[1])
This outputs the same. Try it on the Go Playground. If the input is not guaranteed to have 2 parts, the above indexing ([1]) might panic. Check the length of the slice before doing so.
There is the strings.Split() function which splits the string at the specified sub-string.
There are also the functions Fields(s string) []string, and FieldsFunc(s string, f func(rune) bool) []string.
The former split the string at spaces, and the later uses the given function to determine if the string must be split.
The difference between Split and Fields is that Fields consider multiple consecutive spaces as one split location. strings.Fields(" foo bar baz ")) yields ["foo" "bar" "baz"], and strings.Split(" foo bar baz ", " ") yields ["" "" "foo" "bar" "" "baz" "" "" ""].

How to find a occurrence of a word using grep in a case sensitive manner

I've a file, with below content in it.
foo, FOO, Foo, FOo and i want to search with the lines which matches with the FOo, ie the 4th word in my file.
How can i achieve it using grep ?
simply do
grep "FOo" file
grep by default does a case sensitive search. It matches all the lines that will have FOo in it.
Input:
$ cat file
hello foo
meow Foo
bhow FOo
ding dong Foo
the last one FOO
Output:
$ grep FOo file
bhow FOo
TIP:
If you want to do case-insensitive search use grep -i

korn shell: How to process a command in processing a command?

Hello folks!
First a code I have now:
for CLSGRPID in `${${`/usr/bin/snmpwalk \
-v 1 -c $COMM $HOST $OID.11.1.1.1`##*:}%\n} | xargs` ; do
I'd like to first process
/usr/bin/snmpwalk -v 1 -c $COMM $HOST $OID.11.1.1.1
which used alone returns lines
.2.3.1.2.1.5.11.1.1.1.1 = INTEGER: 1
.2.3.1.2.1.5.11.1.1.1.2 = INTEGER: 2
.2.3.1.2.1.5.11.1.1.1.3 = INTEGER: 3
and then for every return line I'd like to cut it like ${line##*:} and then from the other side ${line%\n} and then all of those lines put to xargs and process it.
So requested output would be
1 2 3
Is it possible? Please get me some ideas how to do it.
net-snmp commands have many options that modify their behaviour and output. I recommend that you read the man pages for each of snmpcmd, snmpwalk, and snmp.conf.
Check the -O option group in snmpwalk (see below).
The -Oqv combination results in a column with just the numbers:
$ clsgrpids=$(
/usr/bin/snmpwalk -Oqv -v 1 -c $COMM $HOST $OID.11.1.1.1
)
$ echo "$clsgrpids"
1
2
3
Remove the quotes to let the shell print a single line:
$ echo $clsgrpids
1 2 3
Here are the remaining options in the -O option group that control the output for snmpwalk:
-O OUTOPTS
Toggle various defaults controlling output display:
0: print leading 0 for single-digit hex characters
a: print all strings in ascii format
b: do not break OID indexes down
e: print enums numerically
E: escape quotes in string indices
f: print full OIDs on output
n: print OIDs numerically
q: quick print for easier parsing
Q: quick print with equal-signs
s: print only last symbolic element of OID
S: print MIB module-id plus last element
t: print timeticks unparsed as numeric integers
T: print human-readable text along with hex strings
u: print OIDs using UCD-style prefix suppression
U: don't print units
v: print values only (not OID = value)
Verification (actual running code)
$ snmpwalk -Ov -v1 -c public localhost sysUptime
Timeticks: (66595) 0:11:05.95
$ snmpwalk -Oqv -v1 -c public localhost sysUptime
0:0:11:35.13
$ snmpwalk -Otqv -v1 -c public localhost sysUptime
70012
You can use awk.
for CLSGRPID in `/usr/bin/snmpwalk -v 1 -c $COMM $HOST $OID.11.1.1.1 | awk '{print $NF}'`; do
echo $CLSGRPID
done

Resources