[GRASS-dev] [GRASS GIS] #2008: grass.script's find_program() can't find modules

#2008: grass.script's find_program() can't find modules
----------------------------+-----------------------------------------------
Reporter: hamish | Owner: grass-dev@…
     Type: defect | Status: new
Priority: normal | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Keywords: find_program() | Platform: Linux
      Cpu: x86-64 |
----------------------------+-----------------------------------------------
Hi,

I'm trying to use grass.script's find_program() function in 6.4.3svn to
check if an addon module exists, but it always returns False, even for
standard modules like r.sun. Testing for other programs on the computer
seems to work ok.

?

thanks,
Hamish

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
----------------------+-----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: closed
  Priority: normal | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: invalid | Keywords: find_program()
  Platform: Linux | Cpu: x86-64
----------------------+-----------------------------------------------------
Changes (by hamish):

  * status: new => closed
  * resolution: => invalid

Comment:

actually it works, what I was missing what that the argument needed to be
in [square] brackets. (why?)

6.4.3svn on linux:
{{{
  import grass.script as grass
  grass.find_program('r.sun,', ['help'])
  True
}}}

I will fix/sync the purely parallel-evolution :slight_smile: version of the test in
the r.hazard.flood script in grass6 addons; the exercise came about since
I was trying to test python addons scripts in grass 6.4 (works on linux
with g.extension, not on Windows due to .bat wrapper files and .py
extension $0 != $0 fun with g.parser).

Hamish

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:1&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
----------------------+-----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: closed
  Priority: normal | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: invalid | Keywords: find_program()
  Platform: Linux | Cpu: x86-64
----------------------+-----------------------------------------------------

Comment(by glynn):

Replying to [comment:1 hamish]:
> actually it works, what I was missing what that the argument needed to
be in [square] brackets. (why?)
Because it's a list of arguments to be passed to the program.

It would be trivial to change the function, i.e.
{{{
-def find_program(pgm, args = ):
+def find_program(pgm, *args):
}}}
but it would also be necessary to change anything which uses it with
arguments, currently:
  * raster/r.colors/thumbnails.py
  * vector/v.colors/thumbnails.py
  * gui/wxpython/gui_core/gselect.py
  * gui/wxpython/gui_core/mapdisp.py
  * gui/wxpython/core/render.py
  * scripts/i.in.spotvgt/i.in.spotvgt.py
  * scripts/r.in.aster/r.in.aster.py
  * scripts/i.spectral/i.spectral.py
  * scripts/g.extension/g.extension.py
  * scripts/v.db.univar/v.db.univar.py

If it's going to be changed, sooner is better than later.

PS: there's no reason for r.colors and v.colors to have near-identical
copies of the thumbnails.py script. It should be moved to tools.

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:2&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------
Changes (by neteler):

  * priority: normal => critical
  * platform: Linux => All
  * status: closed => reopened
  * resolution: invalid =>

Comment:

Replying to [comment:1 hamish]:
> actually it works, what I was missing what that the argument needed to
be in [square] brackets.

For me that fails even after make distclean:

{{{
Welcome to wxGUI Interactive Python Shell 0.9.8

Type "help(grass)" for more GRASS scripting related information.
Type "AddLayer()" to add raster or vector to the layer tree.

Python 2.7.3 (default, Aug 9 2012, 17:23:57)
[GCC 4.7.1 20120720 (Red Hat 4.7.1-5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

import grass.script as grass
grass.find_program('r.sun,', ['help'])
False
grass.version()
{'build_date': '2013-05-19', 'proj4': '4.8.0', 'geos': '', 'sqlite':
'3.7.13', 'libgis_revision': '56211', 'libgis_date': '2013-05-12 13:07:51
+0200 (Sun, 12 May 2013)', 'version': '7.0.svn', 'date': '2013', 'gdal':
'1.9.1', 'revision': '56793M'}
}}}

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:3&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by hamish):

Glynn wrote:
> > +def find_program(pgm, *args):

would that allow it to be called like:
  grass.find_program('r.sun', 'help')

? (I tried a quick test, it didn't like it)

the goal here is to avoid needless extra typing when the only possible
extra options are the $@'s.

> > If it's going to be changed, sooner is better than later.

i.e. before 6.4.3 is released and we have to stay backwards compatible
with the awkward version for the life of 6.x. I think it is still early
enough to change it everything that uses it if we like to, but the window
is closing fast.

Hamish wrote:
> > > grass.find_program('r.sun,', ['help'])
MarkusN:
> For me that fails even after make distclean:

sorry, I had a typo in it, 'r.sun' not 'r.sun,'.

another question with it: should it be changed to test if the program
exists in the PATH and is executable, instead of testing if the program
returns an exit code of 0? (e.g. "gdalwarp" with no options returns '1'
but 'xml2' with no options returns '0')

Hamish

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:4&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by zarch):

Replying to [comment:2 glynn]:
> Replying to [comment:1 hamish]:
> > actually it works, what I was missing what that the argument needed to
be in [square] brackets. (why?)
> Because it's a list of arguments to be passed to the program.
>
> It would be trivial to change the function, i.e.
> {{{
> -def find_program(pgm, args = ):
> +def find_program(pgm, *args):
> }}}

Moreover insert a mutable object as default arguments it's dangerous and
should be avoid, see the example below:

{{{
>>> def myfunc(mylist=):
... mylist.append(5)
... return mylist
...
>>> myfunc()
[5]
>>> myfunc()
[5, 5]
>>> myfunc()
[5, 5, 5]
>>> myfunc()
[5, 5, 5, 5]
}}}

Why not change the function in:

{{{
>>> def find_program(cmd):
... return cmd in grass.get_commands()[0]
...
>>> find_program('r.sun')
True
}}}

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:5&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by hamish):

Replying to [comment:5 zarch]:
> Why not change the function in:
>
> {{{
> >>> def find_program(cmd):
> ... return cmd in grass.get_commands()[0]
> ...
> >>> find_program('r.sun')
> True
> }}}

because we want to use the function as a replacement for `which`, so find
arbitrary helper apps like xml2 and gdalwarp which are not grass modules.
Also, get_commands() is not very robust.

thanks,
Hamish

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:6&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by zarch):

Replying to [comment:6 hamish]:
> Replying to [comment:5 zarch]:
> > Why not change the function in:
> >
> > {{{
> > >>> def find_program(cmd):
> > ... return cmd in grass.get_commands()[0]
> > ...
> > >>> find_program('r.sun')
> > True
> > }}}
>
>
> because we want to use the function as a replacement for `which`, so
> find arbitrary helper apps like xml2 and gdalwarp which are not
> grass modules. Also, get_commands() is not very robust.

What about:

{{{
def which(pgm):
     for p in os.getenv('PATH').split(os.path.pathsep):
         p = os.path.join(p, pgm)
         if os.path.exists(p) and os.access(p, os.X_OK):
             return p
}}}

In python3.3 they add this function in "shutil.which"

http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:7&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by annakrat):

Replying to [comment:7 zarch]:
> What about:
>
> {{{
> def which(pgm):
> for p in os.getenv('PATH').split(os.path.pathsep):
> p = os.path.join(p, pgm)
> if os.path.exists(p) and os.access(p, os.X_OK):
> return p
> }}}
>
> In python3.3 they add this function in "shutil.which"
>
> http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068

The function from the link is more complicated, than what you suggest
above. Why not to use the official one?

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:8&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by zarch):

Replying to [comment:8 annakrat]:
> > http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068
>
> The function from the link is more complicated, than
> what you suggest above. Why not to use the official one?

Yes, I completely agree, the official one is much more save and it is the
one that we should use.

My function was just a proof of concept and then I realize that the python
developers already implemented it.

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:9&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by annakrat):

Replying to [comment:9 zarch]:
> Replying to [comment:8 annakrat]:
> > > http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068
> >
> > The function from the link is more complicated, than
> > what you suggest above. Why not to use the official one?
>
> Yes, I completely agree, the official one is much more save and it is
the one that we should use.
>
> My function was just a proof of concept and then I realize that the
python developers already implemented it.

What about the name? I suggest to keep the old one (find_program) but it's
not strong opinion.

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:10&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by annakrat):

Replying to [comment:10 annakrat]:
> Replying to [comment:9 zarch]:
> > Replying to [comment:8 annakrat]:
> > > > http://hg.python.org/cpython/file/6860263c05b3/Lib/shutil.py#l1068
> > >
> > > The function from the link is more complicated, than
> > > what you suggest above. Why not to use the official one?
> >
> > Yes, I completely agree, the official one is much more save and it is
the one that we should use.
> >
> > My function was just a proof of concept and then I realize that the
python developers already implemented it.
>
> What about the name? I suggest to keep the old one (find_program) but
it's not strong opinion.

ok, please test new implementation of find_program (see link above) r56800
in grass 7. Changes applied also to addons (r56801).

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:11&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by glynn):

Replying to [comment:7 zarch]:

> What about:
>
{{{
def which(pgm):
     for p in os.getenv('PATH').split(os.path.pathsep):
         p = os.path.join(p, pgm)
         if os.path.exists(p) and os.access(p, os.X_OK):
             return p
}}}

That won't work on windows, where PATHEXT also has to be considered.

There are probably other cases which it doesn't handle, e.g. the
executable exists but cannot be run for whatever reason. The previous
implementation (which will be reinstated today unless someone provides a
legitimate reason not to) is robust against such issues.

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:12&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by glynn):

Replying to [comment:4 hamish]:
> Glynn wrote:
> > > +def find_program(pgm, *args):
>
> would that allow it to be called like:
> grass.find_program('r.sun', 'help')

Yes.

> another question with it: should it be changed to test if the program
exists in the PATH and is executable,

No.

> instead of testing if the program returns an exit code of 0? (e.g.
"gdalwarp" with no options returns '1' but 'xml2' with no options returns
'0')

The exit code test can be changed if that's a problem. subprocess.call()
will raise an OSError (with errno=ENOENT) if the executable doesn't exist.

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:13&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by zarch):

Replying to [comment:12 glynn]:
> [snip]
> That won't work on windows, where PATHEXT also has to be considered.
>
> There are probably other cases which it doesn't handle, e.g. the
> executable exists but cannot be run for whatever reason. The previous
> implementation (which will be reinstated today unless someone provides
> a legitimate reason not to) is robust against such issues.

As I already said we don't have to use my implementation...
But why not use the one developed by the python developers?

If the meaning of this function is just a python replacement for the
equivalent "which" bash command. The implementation using Popen, it does
not provide any information about where the program is, therefore is
conceptually different from "which", and personally I find much more clean
ask only for the program, instead of asking for the program AND one
option/parameter...

Moreover asking only for the program is less prone to errors, that can be
introduce by change of the command interface, and this is especially true
if we use this function for library developed by others communities.
But anyway it is not a strong opinion.

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:14&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by annakrat):

Replying to [comment:14 zarch]:
>
> If the meaning of this function is just a python replacement for the
equivalent "which" bash command. The implementation using Popen, it does
not provide any information about where the program is, therefore is
conceptually different from "which", and personally I find much more clean
ask only for the program, instead of asking for the program AND one
option/parameter...

The 'which' implementation of function find_program more suits to the
name. But in this case we probably want to only test if we can run it (and
we don't want to find it).

Is there a way to get rid of the dummy parameters (like 'help') and keep
the interface clean while keeping the previous implementation?

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:15&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by zarch):

Replying to [comment:15 annakrat]:
> The 'which' implementation of function find_program more suits to the
name.
> But in this case we probably want to only test if we can run it (and we
> don't want to find it).

If you want to test if you can run the command or not, why not simply:

{{{
try:
     ret = self._runCommand(RunCommand, prog='g.proj',
                            read=True, flags='p')
except OSError:
     sys.exit(_("GRASS module '%s' not found. Unable to start map "
                "display window.") % 'g.proj')
}}}

instead of:
http://trac.osgeo.org/grass/browser/grass/trunk/gui/wxpython/core/render.py?rev=56445#L454

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:16&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by wenzeslaus):

About the implementation choice, g.manual(.py) is currently not working on
MS Win (7, 8) (probably, mainly) because it uses find_program to check for
the web browser. Running 'explorer' without parameters or with invalid
parameters (almost everything except for file names and URLs) returns non-
zore return code, so find_program considers it as an error and gfatal in
g.manual ends the manual with error. Moreover, it starts file explorer
('This computer' thing).

(Don't be confused with the system web browser in MS Win and explorer. The
explorer program works like e.g. gnome open, it can run another app, in
case of HTML page, and system web browser, e.g. Firefox.)

The result is that there is some group of programs which we cannot run
without consequences, explorer on MS Win is one of them. We can either
introduce another win-specific or even explorer specific conditions and
hope that other programs behaves more like grass commands, or we can use
implementation from Python authors.

I understand that with return code we simple know the most (e.g. the
broken program), but it is necessary to have GRASS so robust, especially
when there can be some consequences (such as opened window of explorer on
MS Win)?

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:17&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by glynn):

Replying to [comment:14 zarch]:

> But why not use the one developed by the python developers?

Because it's an order of magnitude more complex than it needs to be, and
less robust as a consequence. Also, it doesn't test whether the program
actually works. An installed executable with missing shared libraries, an
installed executable for the wrong architecture, or an installed script
with a missing interpreter would all pass the new test. For a locked-down
SELinux system, the correlation between whether the test passes and
whether the program works could be quite close to zero.

The Python community often advocates the use of EAFP ("Easier to Ask for
Forgiveness than Permission", i.e. try it and see if it works) in
preference to LBYL ("Look Before You Leap", i.e. do lots of tests to try
to predict whether something will work).

That principle doesn't appear to have been followed with shutil.which().
But then shutil.which() is trying to mimic the Unix "which" command, which
isn't actually a test, it's a query for the filename corresponding to a
command. What we want is a test.

> If the meaning of this function is just a python replacement for the
equivalent "which" bash command.

Not entirely. We want to know if a particular program is present and
functional on the system. We don't care where it is, but we do care
whether it works (which the replacement doesn't test).

> Moreover asking only for the program is less prone to errors, that can
be introduce by change of the command interface, and this is especially
true if we use this function for library developed by others communities.

In order to test that the program works, and to perform the test with
minimal side-effects, it's sometimes necessary to pass an argument (e.g.
--help or --version). Running the program with no arguments may do too
much (e.g. fire up a GUI).

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:18&gt;
GRASS GIS <http://grass.osgeo.org>

#2008: grass.script's find_program() can't find modules
-----------------------+----------------------------------------------------
  Reporter: hamish | Owner: grass-dev@…
      Type: defect | Status: reopened
  Priority: critical | Milestone: 6.4.4
Component: Python | Version: svn-releasebranch64
Resolution: | Keywords: find_program()
  Platform: All | Cpu: x86-64
-----------------------+----------------------------------------------------

Comment(by glynn):

Replying to [comment:15 annakrat]:

> The 'which' implementation of function find_program more suits to the
name. But in this case we probably want to only test if we can run it (and
we don't want to find it).

Indeed.

> Is there a way to get rid of the dummy parameters (like 'help') and keep
the interface clean while keeping the previous implementation?

Some of the programs which are tested for need a parameter like "help" to
make them return immediately rather than e.g. starting up a GUI. But if we
ignore the exit code, we can probably remove the parameter from many of
them (without e.g. a --help switch, many programs will complain about
missing parameters and return a non-zero exit code).

--
Ticket URL: <https://trac.osgeo.org/grass/ticket/2008#comment:19&gt;
GRASS GIS <http://grass.osgeo.org>