Exporting functions does not expand aliases in subshells

2024-04-11 Thread Philipp Lengauer
Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -g -O2 -flto=auto -ffat-lto-objects -flto=auto
-ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security
-Wall
uname output: Linux TAG009442498805 5.15.0-102-generic #112-Ubuntu SMP Tue
Mar 5 16:50:32 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 5.1
Patch Level: 16
Release Status: release

Description:

When defining aliases and then exporting a function uses these aliases, the
exported function body has the aliases expanded. This makes sense because
we cannot be sure the same aliases exist in the child process where the
exported function will eventually be used. However, when using subshells in
the child process, the aliases are not expanded. This is unexpected
behavior and potentially breaks the function.

Repeat-By:

# this is a minimal example showing where it works and where it doesnt work
alias echo='echo PREFIX'
echo hello world
 # prints "PREFIX hello world" => OK

foo() { echo "hello world"; }
export -f foo
bash -c 'foo'
# prints "PREFIX hello world" => OK

foo() { output="$(echo "hello world")"; printf '%s\n' "$output"; }
export -f 'foo'
# prints "hello world" => NOT OK (PREFIX missing)


Re: Exporting functions does not expand aliases in subshells

2024-04-11 Thread Robert Elz
Date:Thu, 11 Apr 2024 11:05:43 +0200
From:Philipp Lengauer 
Message-ID:  


  | When defining aliases and then exporting a function uses these aliases, the
  | exported function body has the aliases expanded. This makes sense because
  | we cannot be sure the same aliases exist in the child process where the
  | exported function will eventually be used.

That's not the reason it happens though - it happens because aliases
(which are an obnoxious idea, rarely really work the way you'd like
them to - as here - and should be avoided completely) are expanded as
the input is read (they're a lexical construct) when one is detected
at the appropriate position.

ie: when you do:

alias echo='echo PREFIX'
echo hello world

what the lexical analyser hands to the parser is:

alias echo='echo PREFIX'
echo PREFIX hello world

But if you were to write

E=
$E echo hello world

then the alias wouldn't be expanded ("echo" is not in the command
word position when the lexical analyser runs, $E is - when later
run, $E vanishes (as E is empty) and the echo command is run, but
by then it is way too late for aliases to affect anything.

Aliases are stupid!


  | However, when using subshells in
  | the child process, the aliases are not expanded.

I think you'll find they are.   Where they're not expanded is in
command substitutions (in bash anyway) - as (bash) doesn't parse
those until they're executed (well, to be strict, they're parsed
twice, once just to locate their end, and the results are discarded,
and then again when they're used).   Since aliases aren't exported,
(which I hope never changes - or more importantly, that there is never
a way to allow aliases to be imported) when the shell parses the
command substitution (in the shell environment which has imported it)
the environment has no alias, so obviously it cannot work as you expected.

You'd see the same thing if your function was

foo() { eval "echo 'hello world'"; }

as again, the eval string is parsed only when it is executed (and
for eval, unlike the command substitution example, this is important)
and if you export the foo function, the "echo" being in a string
isn't expanded as an alias.   Nor should it be.   (It would be if you
ran this version in a shell which has an alias for echo defined.)

  | This is unexpected behavior and potentially breaks the function.

Using aliases tends to break things.   There's never a really good
(sane) reason to do so, just don't do that.

Instead of
alias echo='echo PREFIX'
just do
echo() { command echo PREFIX "$@"; }

(and export that if that's what you need).

kre




Re: Exporting functions does not expand aliases in subshells

2024-04-11 Thread Kerin Millar
On Thu, 11 Apr 2024, at 10:05 AM, Philipp Lengauer wrote:
> Configuration Information [Automatically generated, do not change]:
> Machine: x86_64
> OS: linux-gnu
> Compiler: gcc
> Compilation CFLAGS: -g -O2 -flto=auto -ffat-lto-objects -flto=auto
> -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security
> -Wall
> uname output: Linux TAG009442498805 5.15.0-102-generic #112-Ubuntu SMP Tue
> Mar 5 16:50:32 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
> Machine Type: x86_64-pc-linux-gnu
>
> Bash Version: 5.1
> Patch Level: 16
> Release Status: release
>
> Description:
>
> When defining aliases and then exporting a function uses these aliases, the
> exported function body has the aliases expanded. This makes sense because
> we cannot be sure the same aliases exist in the child process where the
> exported function will eventually be used. However, when using subshells in
> the child process, the aliases are not expanded. This is unexpected
> behavior and potentially breaks the function.
>
> Repeat-By:
>
> # this is a minimal example showing where it works and where it doesnt work
> alias echo='echo PREFIX'
> echo hello world
>  # prints "PREFIX hello world" => OK

There, the alias is expanded because you are in an interactive shell - where 
the expand_aliases shell option is already enabled - and because it is the 
"first word of a simple command".

> foo() { echo "hello world"; }

Likewise. Further, the alias is expanded at the time of the function's 
declaration.

$ declare -f foo
foo ()
{
echo PREFIX "hello world"
}

> export -f foo
> bash -c 'foo'
> # prints "PREFIX hello world" => OK
>
> foo() { output="$(echo "hello world")"; printf '%s\n' "$output"; }
> export -f 'foo'
> # prints "hello world" => NOT OK (PREFIX missing)

There, the alias does not end up being expanded ...

$ declare -f foo
foo ()
{
output="$(echo "hello world")";
printf '%s\n' "$output"
}

Given that aliases cannot be exported by way of the environment as functions 
can be, it ends up not working as you had anticipated. Even were it the case 
that they could be, you would still have needed to enable expand_aliases in the 
non-interactive shell.

Notwithstanding, I tried declaring the same function in an interactive instance 
of dash and found that the alias within the command substitution does end up 
being expanded, which is in stark contrast to the behaviour of bash.

$ ps -o comm= -p "$$"
dash
$ alias echo='echo PREFIX'
$ foo() { output="$(echo "hello world")"; printf '%s\n' "$output"; }
$ unalias echo
$ echo ok
ok
$ foo
PREFIX hello world

The behaviour of dash seems more logical to me, though I am uncertain as to 
which shell is in the right.

-- 
Kerin Millar



Re: Exporting functions does not expand aliases in subshells

2024-04-11 Thread Robert Elz
Date:Thu, 11 Apr 2024 15:54:21 +0100
From:"Kerin Millar" 
Message-ID:  <57e307d0-6a16-443b-82ce-adf540f96...@app.fastmail.com>

  | The behaviour of dash seems more logical to me,
  | though I am uncertain as to which shell is in the right.

In as much as how aliases work, who cares?   They're insane anyway.

As for how/when the command substitutions are parsed, both are OK.

In some sense, according to POSIX, the bash way is perhaps slightly
more in accordance with how they assume expansions happen (which is
what a command substitution is) - but aside from aliases, it makes
no difference at all which way is used - the dash (ash derived shells
in general) way means parsing the command substitution only once
(which means guaranteed consistent results), whereas bash needs to
parse it perhaps multiple times - once where its only purpose is
to locate the end of the cmdsub, and then once every time the
command containing the cmdsub is executed (of course, the parse
time is likely dwarfed by execution time of the cmdsub, but wasting
time parsing things over and over again, where apart from possible
bizarre alias effects, the results will be the same every time, seems
wasteful to me).

For how aliases can mess things up, with the bash way of parsing
command substitutions, if we do:

foo() { echo hello; X=$(echo goodbye); echo "$X"; }

and just run foo then we get "hello", "goodbye" (on successive lines).

If we then do

alias echo='echo say'

and run foo again (without redefining it) then we get "hello" "say goodbye"

as the first "echo" was already parsed, the (later) alias can't
affect that one, but the one in the command substitution hasn't
been (properly) parsed yet, so the alias affects that.

In the ash shell style, once a function is defined, it is defined,
and adding or changing aliases (other than for the name of the
function) later will not effect anything except an included "eval"
(or '.' command) (including a trap execution, which is just an eval
at a less predictable time).

kre




Re: Exporting functions does not expand aliases in subshells

2024-04-11 Thread Oğuz
On Thursday, April 11, 2024, Kerin Millar  wrote:
>
> Notwithstanding, I tried declaring the same function in an interactive
> instance of dash and found that the alias within the command substitution
> does end up being expanded, which is in stark contrast to the behaviour of
> bash.
>

Bash's behavior matches that of dash in POSIX mode. This is documented here
in the 8th item https://tiswww.case.edu/php/chet/bash/POSIX


-- 
Oğuz


Re: Exporting functions does not expand aliases in subshells

2024-04-11 Thread Kerin Millar
On Thu, 11 Apr 2024, at 4:57 PM, Oğuz wrote:
> On Thursday, April 11, 2024, Kerin Millar  wrote: 
> Notwithstanding, I tried declaring the same function in an interactive 
> instance of dash and found that the alias within the command 
> substitution does end up being expanded, which is in stark contrast to 
> the behaviour of bash.
>
> Bash's behavior matches that of dash in POSIX mode. This is documented 
> here in the 8th item https://tiswww.case.edu/php/chet/bash/POSIX

Thank you. I thought that I had tested the posix mode properly before posting 
but I had not.

-- 
Kerin Millar



Re: [PATCH v2 04/18] doc/bash.1: improve typography of ellipses

2024-04-11 Thread Martin D Kealey
On Thu, 1 Feb 2024 at 07:54, G. Branden Robinson <
g.branden.robin...@gmail.com> wrote:

> v2: Prevent confclit with PATCH v2 01/18.
> Apply ellipsis advice from groff_man_style(7).
> • The dummy character escape sequence \& follows the ellipsis when further
> text will follow after space on the output line, keeping its last period
> from being interpreted as the end of a sentence and causing additional
> inter‐sentence space to be placed after it.
>

Is there a reason why we're still using a triple period/full-stop “...”
(\u002e) instead of an actual ellipsis “…” (\u2026)?

-Martin


Re: [PATCH v2 04/18] doc/bash.1: improve typography of ellipses

2024-04-11 Thread G. Branden Robinson
Hi Martin,

At 2024-04-12T14:55:22+1200, Martin D Kealey wrote:
> On Thu, 1 Feb 2024 at 07:54, G. Branden Robinson <
> g.branden.robin...@gmail.com> wrote:
> 
> > v2: Prevent confclit with PATCH v2 01/18.
> > Apply ellipsis advice from groff_man_style(7).
> > • The dummy character escape sequence \& follows the ellipsis when
> > further text will follow after space on the output line, keeping its
> > last period from being interpreted as the end of a sentence and
> > causing additional inter‐sentence space to be placed after it.
> 
> Is there a reason why we're still using a triple period/full-stop
> “...” (\u002e) instead of an actual ellipsis “…” (\u2026)?

Yes.  Portability to troffs that don't support GNU troff extensions.

 • Why doesn’t the package provide a string to insert an ellipsis?

[...]
   There is an ellipsis code point in Unicode, and some fonts have
   an ellipsis glyph, which some man pages have accessed in a non‐
   portable way with the font‐dependent \N escape sequence.  We
   discourage the use of these; on terminals, they may crowd the
   dots into a half‐width character cell, and will not render at all
   if the output device doesn’t have the glyph.  In syntax synopses,
   missing ellipses can mislead the reader.  Dots and space are
   universally supported.

When AT&T troff finally dies or is abandoned by projects (like Bash and
ncurses) currently maintaining portability to System V hosts (like
Solaris 10), that might be a good time to reconsider the foregoing
point.

But likely not with a *roff _string_, for reasons I elided.

Regards,
Branden


signature.asc
Description: PGP signature