Exporting functions does not expand aliases in subshells
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
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
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
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
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
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
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
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