On Fri, 19 Aug 2022 at 14:13, David Wright <deb...@lionunicorn.co.uk> wrote:
> On Thu 18 Aug 2022 at 06:58:20 (-0400), Greg Wooledge wrote:
> > On Wed, Aug 17, 2022 at 10:58:17PM -0500, David Wright wrote:
> > > $ type soxy
> > > soxy is a function
> > > soxy ()
> > > {
> > >     [ -z "$1" ] && printf '%s\n' "Usage:    ${FUNCNAME[0]} 
> > > path-to/sound-file-of-any-type [trim 20 2]
> > >     runs sox to play the file with any arguments given.
> > >     The example above reminds you to put the full argument." 1>&2 && 
> > > return 1;
> > >     local From="$1";
> > >     shift;
> > >     sox -q "$From" -t alsa default "$@"
> > > }
> >
> > Pedantic note: your error checking can fail.  If the printf fails for
> > some reason (e.g. because stderr has been closed, or is redirected to
> > a file on a file system that's full), the return won't execute.
>
> True—I guess I was willing to carry the risk. Thanks for noting that.
>
> > It's best just to use "if" in the normal way:
> >
> >   if [ -z "$1" ]; then
> >     printf ...
> >     return 1
> >   fi
> >
> > That way, the return will still be executed even if the printf fails.
> >
> > If you *insist* on using && because you think it's tres chic or something,
> > then you need to use a command group:
> >
> >   [ -z "$1" ] && {
> >     printf ...
> >     return 1
> >   }
> >
> > But this is not the recommended practice.
>
> Not so much très chic as bijou⁰. I can edit, eg
> [ -z "$Thepagecount" ] && printf '%s\n' "$1 has no pages!" ¹>&2 && return 1
> to
> [ -z "$Thepagecount" ] && { printf '%s\n' "$1 has no pages!" ¹>&2 ; return 1 
> ; }
> very easily. (I think I have about 350 such constructions in ~/.bash*.)
>
> The attraction of a one-liner is partly because of screens
> being around four times wider than high (characterwise).
> Wouldn't it be nice if bash had Perl's die ….

Hi David

Some further bash script thoughts ...

Considating hundreds of identical "printf '%s\n' 1>&2" constructs
into one function seems like it would remove a lot of visual noise,
and streamline reading and writing that code. My style would be:

msg_stderr() {
    printf '%s\n' "$@" 1>&2
}

soxy() {
    if [[ -z "$1" ]] ; then
        msg_stderr "Usage:    ${FUNCNAME[0]}
path-to/sound-file-of-any-type [trim 20 2]
runs sox to play the file with any arguments given.
The example above reminds you to put the full argument."
        return 1
    fi
}

I find that style easier to read and reason about, but it would involve
more editing of your existing code.

Aside: I see that you are using ${FUNCNAME[0]}, so bash features
are acceptable. Using an array to pass the message lines to the
same function would preserve the code indentation, which
further improves readability for me.

soxy() {
    if [[ -z "$1" ]] ; then
        local m=(
            "Usage:    ${FUNCNAME[0]} path-to/sound-file-of-any-type
[trim 20 2]"
            "runs sox to play the file with any arguments given."
            "The example above reminds you to put the full argument."
        )
        msg_stderr "${m[@]}"
        return 1
    fi
}

If you want to stay close to the oneliner style that you already
have, it could be done like this:

die() {
    printf '%s\n' "$@" 1>&2
    return 0
}

soxy() {
    [[ -z "$1" ]] && die "message" && return 1
}

This avoids the issue of unexpected errors in printf, makes all
return values explicit, and avoids mixing && and || operators.
And hopefully not much trouble for you to edit, either :)

Reply via email to