dash.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Harald van Dijk <harald@gigawatt.nl>
To: earnestly <zibeon@gmail.com>, dash@vger.kernel.org
Subject: Re: getopts appears to not be shifting $@ when consuming options
Date: Fri, 29 Jan 2021 20:15:07 +0000	[thread overview]
Message-ID: <c5b375e0-e6b4-df94-3dbd-26c05ecbe688@gigawatt.nl> (raw)
In-Reply-To: <YBRTFXGM6pLwR5hg@teapot>

Hi,

On 29/01/2021 18:25, earnestly wrote:
> In this example dash will repeatedly append 'attr=foo' to the list of
> parameters in an infinite loop:
> 
>      #!/bin/dash -x
> 
>      while getopts :a: arg -a foo -a bar; do
>          case $arg in
>              a) set -- "$@" attr="$OPTARG"
>          esac
>      done
>      shift "$((OPTIND - 1))"
> 
> Instead I expected this to result in parameter list containing
> 'attr=foo' and 'attr=bar'.

You are correct in your expectation, I believe: ending the loop after 
processing both arguments is what your script should do.

The reason that dash is behaving the way it does is that dash resets the 
getopts state when the positional arguments are changed by the set or 
shift commands. The getopts state is also reset when the positional 
arguments are changed because of a function call, but restored after the 
function returns. This is something shared across ash-based shells; you 
will also find it in FreeBSD's /bin/sh, and if I am not misreading the 
code, NetBSD's /bin/sh as well.

Although there are certainly cases where this behaviour is useful, 
especially the part where it saves and restores state for function 
calls, there are also where it is not, such as yours. Additionally, it 
appears to be in conflict with POSIX, which requires the getopts state 
to be preserved as long as it continues to be called with the same 
arguments: only resetting OPTIND to 1 is specified to reset the state to 
allow arguments to be parsed anew.

I would suggest that if this is changed to conform to POSIX, a 
non-standard method should remain available to allow shell functions to 
use getopts internally, including when the getopts loop in the function 
calls other functions that themselves use getopts, to ensure that any 
existing scripts broken by the change can be easily updated. One way to 
achieve that could be to special-case "local OPTIND=1" so that when the 
function returns, it restores not just the value of OPTIND, but uses 
that moment to additionally restore the extra internal state.

Cheers,
Harald van Dijk

  reply	other threads:[~2021-01-29 20:15 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-29 18:25 getopts appears to not be shifting $@ when consuming options earnestly
2021-01-29 20:15 ` Harald van Dijk [this message]
2021-01-30 15:31   ` Harald van Dijk
2021-01-29 20:36 ` Jilles Tjoelker
2021-01-29 21:19   ` Harald van Dijk
2021-01-29 22:25   ` earnestly
2021-01-30  6:39     ` Vladimir N. Oleynik
2021-01-30  7:36     ` Vladimir N. Oleynik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c5b375e0-e6b4-df94-3dbd-26c05ecbe688@gigawatt.nl \
    --to=harald@gigawatt.nl \
    --cc=dash@vger.kernel.org \
    --cc=zibeon@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).