Specify an option one or more times in docopt - docopt

I have a program (in python but that shouldn't matter) that takes some options one or more times, such as:
# Valid cases:
python test.py --o 1 --p a <file>
python test.py --o 1 --o 2 --p a --p b <file>
# Invalid:
python test.py --o a <file>
python test.py --p a <file>
python test.py <file>
This script works:
#!/usr/bin/env python2.7
"""Test
Usage:
test.py --o=<arg> [--o=<arg>...] --p=<arg> [--p=<arg>...] <file>
"""
from docopt import docopt
if __name__ == '__main__':
arguments = docopt(__doc__, version='Test 1.0')
print(arguments)
However the option is repeated and it feels pretty ugly. I tried the following:
test.py --o=<arg>[...] --p=<arg>[...] <file>
test.py (--o=<arg>)[...] (--p=<arg>)[...] <file>
test.py (--o=<arg>[...]) (--p=<arg>[...]) <file>
But none of them worked. The alternative would be to make the option fully optionnal and check its value in the program:
test.py [--o=<arg>...] [--p=<arg>...] <file>
...
if len(arguments["--o"]) < 1:
raise ValueError("One or more --o required")
if len(arguments["--p"]) < 1:
raise ValueError("One or more --p required")
But I feel there should be an easy solution to do that with docopt directly. Is there a beautiful way to do it?

A bit late, but
Usage:
test.py (--o=<arg>)... (--p=<arg>)... <file>
does what you want.
Usage:
test.py (--o=<arg>...) (--p=<arg>...) <file>
Work too.

Related

How to execute this command in systemd servicefile?

Ok, so I have this command that turns off my touchscreen. It works when I execute it in a root shell.
So this works:
sudo su
/usr/bin/echo $(ls /sys/bus/hid/drivers/hid-multitouch | awk NR==1'{print $1}') > /sys/bus/hid/drivers/hid-multitouch/unbind
And then my touchscreen stops working, which is the result that I wanted.
Now I want to make a touchscreen.service file to execute this on every boot. So in the service file I include:
ExecStart=/usr/bin/echo $(ls /sys/bus/hid/drivers/hid-multitouch | awk NR==1'{print $1}') > /sys/bus/hid/drivers/hid-multitouch/unbind
However it isn't working > nor throwing any errors that I've been able to catch.
I do know from earlier fidlings with .service files that I might actually need to use /usr/bin/sh -c, so I have also tried:
ExecStart=/usr/bin/sh -c "/usr/bin/echo $(ls /sys/bus/hid/drivers/hid-multitouch | awk NR==1'{print $1}') > /sys/bus/hid/drivers/hid-multitouch/unbind"
Yet this also doesn't work.. maybe because of the awk NR==1'{print $1}'part? I have also tried replacing it with awk NR==1'\''{print $1}'\''but again it fails to work.
Does anyone have any ideas on how to get the command that is working in my root cli environment to also work as a systemd service?
To start with,
The syntax of the awk command is just wrong. The quotes are incorrectly placed. The part NR == 1 is part of the awk command to indicate the first line record in the file, i.e.
awk NR==1'{print $1}'
# ^^^^^^^ should be within quotes
awk 'NR == 1 { print $1 }'
Your sequence of echo, ls and the command substitution $(..) doesn't look right. You are effectively echo-ing the literal string /sys/bus/hid/drivers/hid-multitouch (if ls finds the file at that path) over to the pipe and awk just writes that to the /sys/bus/hid/drivers/hid-multitouch/unbind file which might not be your desired action. You just needed to do run the command on the file directly as
awk 'NR == 1 { print $1 }' /sys/bus/hid/drivers/hid-multitouch > /sys/bus/hid/drivers/hid-multitouch/unbind
Now that, that the awk command is fixed, you have two options to run the above command as part of systemd, either put your command in a script or run the command directly. For putting it in a script refer to the Unix.SE answer Where do I put scripts executed by systemd units?. As for running the command directly in ExecStart. Aside from using /bin/sh also use the path /bin/awk
So putting it together and using /bin/ over /usr/bin, you can do below. This command uses ".." over awk script and needs escape of $1
ExecStart=/bin/sh -c '/bin/awk "NR == 1 { print \$1 }" /sys/bus/hid/drivers/hid-multitouch > /sys/bus/hid/drivers/hid-multitouch/unbind'

Ouput to be piped must be shared as 2 by 2 arguments

How to share the piped output 2 by 2 arguments, only the first argument is for a printout command, i.e. echo to stdout/stderr, and the second argument for another command (let it be CMD), by making use of xargs
Ouput ie. the source to be piped can be as either the 2 arguments in one single line or 1 arg for 1 line, below all only illustration:
echo -e 'foo bar\nfoo1 bar1\nfoo2 bar2\n' # ... much more
or
echo -e 'foo\nbar\nfoo1\nbar1\nfoo2\bar2\n'
so how is
echo -e 'foo bar\nfoo1 bar1\nfoo2 bar2\n' |xargs echo |xargs CMD
supposed to be in real?
expected printout stdout/stderr result
foo
{output of CMD being fed with bar}
foo1
{output of CMD being fed with bar1}
foo2
{output of CMD being fed with bar2}
# ...
You can use xargs -n2 to do this by-2 args, then ab-use a shell scriptlet to handle $1 and $2 as e.g. (using ls as CMD just as example):
$ echo -e 'foo\nbar\nfoo1\nbar1\nfoo2\nbar2'|xargs -n2 sh -c 'echo $1; ls $2' --
foo
ls: cannot access 'bar': No such file or directory
foo1
ls: cannot access 'bar1': No such file or directory
foo2
ls: cannot access 'bar2': No such file or directory
NOTE the needed -- as last xargs argument

Why is this docopt string not working either with or without optional args?

Here is the complete docopt string I used:
foo.
Usage:
foo [options] <file> -o <output>
foo --help | --version
Options:
-h, --help print this help message.
--target <target> target.
--version print the version.
According to the official parser, either foo a -o b or foo --target abc a -o b is not correctly parsed. What could be possible reasons for this case? Any help would be appreciated.
I'm not entirely sure about the allowed combinations of options for your script, but here's something that should be close.
Just for fun, I wrote a script that has similar options to yours to test this out with the latest docopts.
I found it simplest to write just [options] in the main Usage section, and have all the options below as alternatives, with no specific combinations required.
I'm on macOS so I'm using bash 3.2 (with patched docopts.sh to fix some Mac issues). You can avoid some of the code in this script if you're on bash 4.x - see the commented-out section with --auto option and docopt_print_ARGS function. Currently you would need bash 4.x to avoid patching docopts.sh.
#!/bin/bash
#
# foo.
#
# Usage:
# foo [options] <file>
# foo --help | --version
#
# Options:
# -t, --target <target> target.
# -o, --output <output> output.
# -h, --help print this help message.
# --version print the version.
#
# bash 3.2 (patched docopts.sh) - include set -x to see the args easily
source docopts.sh
usage=$(docopt_get_help_string "$0")
set -x
eval "$(docopts -G ARGS -V "$VERSION" -h "$usage" : "$#")"
# On bash 4.x, replace preceding section with this, or use -A instead of -G above
# source docopts.sh --auto "$#"
# docopt_print_ARGS
This parses the Usage section OK and processes command lines such as:
foo --target a -o b file1
foo --target a --output b file1
foo --target a file1
Partial output with set -x to show args processed correctly:
$ ./foo --target a file1 --output b
...
++ ARGS_target=a
++ ARGS_output=b
++ ARGS_file=file1
++ ARGS_help=false
++ ARGS_version=false
Thanks for #RichVel's efforts. Yesterday I finally found out the underlying (stupid) cause for this problem.
In the official online parser the first part, i.e. foo shouldn't be used. --target abc a -o b works fine in the online example.
Regarding my question, the bug actually comes from that docopt.rs stores --target abc in flag_target instead of arg_target.

Why does `ipython foo.py bar.py` only print `foo.py`'s output?

The IPython 0.13.1 documentation says:
$ ipython -h
...
Usage
ipython [subcommand] [options] [files]
If invoked with no options, it executes all the files listed in sequence
and exits, use -i to enter interactive mode after running the files.
...
I have two files foo.py and bar.py.
foo.py:
print "Hi, I'm foo."
bar.py:
print "Hi, I'm bar."
I expect the following to print both files output, in the corresponding order. Instead I only get the output from the first file given on the command line.
$ ipython foo.py bar.py
Hi, I'm foo.
$ ipython bar.py foo.py
Hi, I'm bar.
Is that an implementation bug, a documentation bug, or user misunderstanding? If the latter, what should I do instead?
This is a documentation failure, fixed by this Pull Request.
The command
$> ipython [-i] script.py script2.py ...
behaves exactly the same as the command
$> python [-i] script.py script2.py ...
In that, script.py is run, with sys.argv of ['script.py', 'script2.py', '...'],
and if -i is specified, it drops into an interactive session after running the script.

Call a Mathematica program from the command line, with command-line args, stdin, stdout, and stderr

If you have Mathematica code in foo.m, Mathematica can be invoked with -noprompt
and with -initfile foo.m
(or -run "<<foo.m")
and the command line arguments are available in $CommandLine (with extra junk in there) but is there a way to just have some mathematica code like
#!/usr/bin/env MathKernel
x = 2+2;
Print[x];
Print["There were ", Length[ARGV], " args passed in on the command line."];
linesFromStdin = readList[];
etc.
and chmod it executable and run it? In other words, how does one use Mathematica like any other scripting language (Perl, Python, Ruby, etc)?
MASH -- Mathematica Scripting Hack -- will do this.
Since Mathematica version 6, the following perl script suffices:
http://ai.eecs.umich.edu/people/dreeves/mash/mash.pl
For previous Mathematica versions, a C program is needed:
http://ai.eecs.umich.edu/people/dreeves/mash/pre6
UPDATE: At long last, Mathematica 8 supports this natively with the "-script" command-line option:
http://www.wolfram.com/mathematica/new-in-8/mathematica-shell-scripts/
Here is a solution that does not require an additional helper script. You can use the following shebang to directly invoke the Mathematica kernel:
#!/bin/sh
exec <"$0" || exit; read; read; exec /usr/local/bin/math -noprompt "$#" | sed '/^$/d'; exit
(* Mathematica code starts here *)
x = 2+2;
Print[x];
The shebang code skips the first two lines of the script and feeds the rest to the Mathematica kernel as standard input. The sed command drops empty lines produced by the kernel.
This hack is not as versatile as MASH. Because the Mathematica code is read from stdin you cannot use stdin for user input, i.e., the functions Input and InputString do not work.
Assuming you add the Mathematica binaries to the PATH environment variable in ~/.profile,
export PATH=$PATH:/Applications/Mathematica.app/Contents/MacOS
Then you just write this shebang line in your Mathematica scripts.
#!/usr/bin/env MathKernel -script
Now you can dot-slash your scripts.
$ cat hello.ma
#!/usr/bin/env MathKernel -script
Print["Hello World!"]
$ chmod a+x hello.ma
$ ./hello.ma
"Hello World!"
Tested with Mathematica 8.0.
Minor bug: Mathematica surrounds Print[s] with quotes in Windows and Mac OS X, but not Linux. WTF?
Try
-initfile filename
And put the exit command into your program
I found another solution that worked for me.
Save the code in a .m file, then run it like this: MathKernel -noprompt -run “<
This is the link: http://bergmanlab.smith.man.ac.uk/?p=38
For mathematica 7
$ cat test.m
#!/bin/bash
MathKernel -noprompt -run < <( cat $0| sed -e '1,4d' ) | sed '1d'
exit 0
### code start Here ... ###
Print["Hello World!"]
X=7
X*5
Usage:
$ chmod +x test.m
$ ./test.m
"Hello World!"
7
35