On 15.04.2024 14:22, James Harris wrote:
For someone who is relatively new to Unix shell scripting (me) some
advice would be more than welcome on where to begin.
I have two main queries:
Q1) How can one write a script which is maximally compatible with
different systems?
There's various grades of "portable". These days I would - when
striving for portability - use the _POSIX_ features as base of
your programming. That means; not Bourne shell. If you don't
want to learn what's defined in POSIX you should probably use a
shell that most closely resembles the POSIX subset, maybe 'dash'.
Or get the book from Bolsky/Korn that has an appendix with the
feature comparisons Bourne/POSIX/ksh88/ksh93/...
Personally, I have a less restricted view on "portability". I
don't want to miss the modern features, specifically those that
can all be found in the prominent shells (ksh, zsh, bash). From
those I'd pick what will be available in your systems' contexts.
On Linux you usually have all these shells available, but in a
commercial context (from those three shells) you may have only
ksh available. One "problem" with those shells is that each will
provide own features that the other two shells won't support. So
either you'll have to spend some time learning the differences
or if you write your scripts run them through all three shells
to let the shell provide the information.
There's other factor like execution speed (ksh), consistent new
[non-standard] concepts (zsh), large community (bash), that may
influence your decision.
Personally I use Kornshell which has the richest feature set and
is the fastest; specifically Martijn Dekker's branch (of the
original AT&T) "ksh93u+m". I try to use mostly features also
available in bash and zsh, but wouldn't take that too strict.
I am thinking to write in /the language of/ the Bourne shell, if
feasible, so that it could be run by either the Bourne shell or Bash,
etc? (Ideally, the shebang line would be #!/bin/sh.)
Regularly "/bin/sh" nowadays refers to a POSIX shell.
But the '#!' line's purpose is to define any available interpreter.
Just be sure that you specify the shell language used whenever
deviating from POSIX features.
Or is Bash now so universal that there's no point any longer in writing
for anything else?
See above.
Q2) How does one go about handling arguments in preferably a simple but
universal way?
My first idea was to iterate over arguments with such as
while [ $# -gt 0 ]
do
...
shift
done;
and from that to (a) note any switches and (b) build up an array of
positional parameters. However, I gather the Bourne shell has no arrays
(other than the parameters themselves) so that won't work.
Arrays can be populated by the argument list in one go, e.g. by
a=( "$@" )
(but that may not be what you want).
I read up on getopts
It's the right tool.
but from tests it seems to require that switches
precede arguments rather than allowing them to be specified after, so
that doesn't seem very good, either.
That's the usual convention, first come the options (with optional
arguments), then the non-option arguments. Here's a syntax example
yagol [-s] [-w width] [-h height] [-g[ngen]] [-d density]
[-i infile] [-o outfile] [-r random-seed] [-u rule]
[-k|-t[sec]|-l|-f] [-p|-n|-c] [-a[gen]] [-m[rate]]
(this one just with options and no further arguments).
Of course it uses ksh's getopts - but note that ksh's getopts is
not portable - because it simplifies processing a lot (and it also
implies a usage and help information).
Online tutorials show different ways to handle this and few talk about
which shell to use for this case so I thought I would ask you guys for
suggestions.
My clear getopts favorite (but not only with this featute) is the
original AT&T Kornshell (in form of above mentioned "u+m" version).
My requirement just now is, in fact, so simple that I don't need a
universal way to handle things but ISTM best to start with an approach
that will scale over time, if there is one.
This is an excellent thought; it's very typical that you start
with trivial samples, and then (own and foreign) demands come up
to extend it, and at some point you have to do some refactoring
(unless you started with a more general approach). (Above yagol
example also started with only four options.)
So any guidance on how to get started would be appreciated!
Hope it helps. - Feel free to come back with more questions.
Janis