wryun / es-shell

es: a shell with higher-order functions
http://wryun.github.io/es-shell/
Other
313 stars 26 forks source link

Inconsistent getopt behavior depending on the local implementation #60

Closed jpco closed 11 months ago

jpco commented 1 year ago

This seems to be all GNU's fault. They decided to be "helpful" by making GNU getopt parse ALL options in argv, rather than stopping at the first non-option arg as the POSIX spec states. Several libcs have imitated GNU in doing this. Several others follow POSIX.

This is 1. inconsistent with every other shell, 2. inconsistent with es' own man page, and 3. badly surprising in certain cases. See this example:

; ./coolscript foo bar
My cool script! argv: foo bar
; ./coolscript -x
{echo My cool script^! argv: $*}
My cool script! argv:
; cat coolscript 
#!/usr/local/bin/es
echo My cool script! argv: $*
;

This can be worked around by adding "+" to the front of the optstring arg to getopt(), but that's an imperfect solution because some implementations will barf if the "+" is present. (You can also set POSIXLY_CORRECT=1 in the env when invoking es, but ... ew.)

I see two good options here: 1. roll es' own opt-getting logic (it's not terribly hard to do); or 2. set up some autoconf magic to detect whether the local getopt understands "+".

memreflect commented 1 year ago

I favor an internal POSIX getopt implementation. As you said, it's not difficult, and it's a bad idea to rely on GNU getopt behavior like the + prefix or the POSIXLY_CORRECT environment variable when compiling on other systems that aren't necessarily GNU-based.


To make things clearer for those who might be confused about what's going on:

  1. ./coolscript -x is executed by a user.
  2. #!/usr/local/bin/es is seen in ./coolscript, so /usr/local/bin/es ./coolscript -x is what's actually executed by the system.
  3. Some implementations of getopt(3) reorder the argument list so that recognized options appear first, with the GNU C Library's implementation being the prime example. es uses getopt() to parse options, resulting in the reordered /usr/local/bin/es -x ./coolscript.
  4. es sees -x and enables tracing, then moves to the next argument ./coolscript, which it begins executing as a script.
  5. ./coolscript never sees the -x option that was intended for it.