18
$\begingroup$

Former question: How to use wolframscript to develop flexible command-line tools?

I am currently changing a notebook into a wolframscript for a "production" environment, where I plan to pack the script as a standard CLI tool. However I do not know how to implement such an interface, where boolean, switch-like, argument accepting and other sorts of options are supported.

In bash python and many other scripting languages, either the parsing functionality is built-in, or there exists (semi-)official parsing modules for this purpose, named getopt argparse or similar.

As of my knowledge and of Jan 2021, there is no such libraries for wolframscript yet. The GetOpts package by @McSaks, a candidate for this, has been out of maintenance for years, and no longer suffice modern needs.

There does exist a -args option in the grammar, supplied to the executed command, after the main -c|-f|-api|-fun parameter for the interpreter (which did not seem to exist when the related questions on SE were asked). But it is de facto not documented, thus leaving me confused.


Related resources

Based on the answer by @Mr Puh, I think the wording of my question may not be clear enough, and the following resources may be helpful for discussion:

  1. C library function getopt - Wikipedia
  2. C++ Boost.Program_options
  3. Built-in Unix shell command getopts - Wikipedia
  4. Python 3 standard library | getopt — C-style parser for command line options (not recommended)
  5. Python 3 standard library | argparse — Parser for command-line options, arguments and sub-commands
  6. Python 3 HOWTOs | Argparse Tutorial
  7. Google Commandline Flags
  8. C++ 3rd party library argparse by @p-ranav
  9. Haskell command line option parsers

Related questions (sorted by relevance)

  1. Passing arguments into a script in WolframScript
  2. Creating flags to use with wolframscript?
  3. Parse command line arguments
  4. Mathematica script - passing command line arguments

Since this thread is not receiving extensive replies, I now wonder what is the best practice of wolframscript. What's the use intended by WRI? What's the most common official practice?

$\endgroup$
5
  • 1
    $\begingroup$ I would try to have the script interpret the argument as a Wolfram language expression. For example, the user could pass a list of rules. $\endgroup$ Jan 31, 2021 at 23:28
  • 1
    $\begingroup$ I'm not familiar with CLI but interested in the question, could you please add a hypothetical example on how would you like it to work or look like? $\endgroup$
    – Silvia
    Feb 10, 2021 at 7:34
  • 3
    $\begingroup$ An ability to provide a local APIFunction seems good enough already. It contains all interpreter features for parsing any type of argument. So just Put[APIFunction[...], LocalObject["yourscriptname"]] and then wolframscript -api yourscriptname -local -args x=1 y=string z='10 meters'. $\endgroup$
    – swish
    Feb 10, 2021 at 7:59
  • $\begingroup$ @swish Thanks a lot for this comment, the ability to pass arguments as an association is quite flexible. But since I am not familiar with APIFunctions, after some tweaking, I am still a little confused with their usage. Is it that only built-in or pure functions can be used in APIfunctions? $\endgroup$
    – Gravifer
    Feb 12, 2021 at 13:37
  • $\begingroup$ @swish Really sorry but I still have questions... I wonder how the Wolfram system recognizes a LocalObject as a API? We know that a LocalObject is a directory in the $Path, with a object.wl file and additional files with definitions. With the code you provided, there will be a put.wl which contains a singal APIFunction expression. $\endgroup$
    – Gravifer
    Mar 14, 2021 at 14:53

2 Answers 2

2
$\begingroup$

Following the route suggested by swish, I ended up modifying the LocalObject file created by Put. The reason is that my script contains several delayed definitions that are otherwise evaluated by Put.

Here's an example of the content of a LocalObject["example"] data-file:

(* Definition added later *)
f[number_, name_] := "Hello " <> name <> "! Your number is: " <> number

(* This was created by Put[APIFunction[...], LocalObject["example"]] *)
APIFunction[{"number"->"String","name"->"String"}, f[#number, #name]& ]

which can later be called with wolframscript -api example -local -args name='Pippo' number=31.

I'm sure there's a better way of writing an expression unevaluated without having to manually edit the local object file. I tried with HoldForm – or ToExpression[expr, InputForm, Hold] – but then one should use ReleaseHold to evaluate the expression and, honestly, I don't know how to do it inside the script.

$\endgroup$
-1
$\begingroup$

As there are no answers yet, I'd like to share how I use wolframscript from the command line, please correct me if I am wrong.

Here are two possibilities I know of:

Converting a notebook to a script

  1. You create a notebook (.nb), convert all the cells relevant to initialization cells and save it as an .m file.

  2. You can use wolframscript -script scriptname arg1 arg2 arg3 now and arg1 will be available as within the script as $ScriptCommandLine[[2]]]. Also check this documentation.

Use python

There is a wolframlanguage plugin for python now. You can use this to call functions in the namespace of one of your wolfram packages. So first write a package, then in python do something like this:

from wolframclient.evaluation import WolframLanguageSession
from wolframclient.language import wl

with WolframLanguageSession() as s:

    # Locate the mathematica package to install
    s.evaluate(wl.SetDirectory(s.evaluate(wl.Directory())+path_to_mathematica_m))
    s.evaluate(wl.Import("YOUR_PACKAGE.m"))
    
    # This calls the wolfram language function with arguments
    output_expression = wl.YOUR_PACKAGE.Function1(an_array.tolist(),arg2,arg3)

    output = s.evaluate(output_expression)

    # Then do something with the output. E.g. Export it to a file:
    s.evaluate(wl.Export(exportpath,output))

This allows you to use the wolframlanguage via python and thus you can use all the tools you have for python also for the wolframlanguage.

$\endgroup$
3
  • $\begingroup$ (Former comment edited) Thank you for your reply but I think you may be beside the point. My intention is to use the wolframscript interpreter (not python or others as such) from the OS shell upon a .wls script, and provide it with a Unix-like (preferably bash oriented) CLI, i.e., systematic man construction, named (keyword), optional, switch and array parameters and other stuff. $\endgroup$
    – Gravifer
    Feb 9, 2021 at 10:01
  • 1
    $\begingroup$ Official documentation only allow positional command-line arguments for local user defined scripts. This is sufficient if the script merely contains a simple function, but is found wanting for more sophisticated circumstances. $\endgroup$
    – Gravifer
    Feb 9, 2021 at 10:07
  • $\begingroup$ I personally have been using the python invocation approach for a while now, but compicated invocations are hard to debug and error-prone. As you can see, this situation can be greatly improved if the WLS script have a flexible CLI. $\endgroup$
    – Gravifer
    Feb 9, 2021 at 10:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.