URL: <https://savannah.gnu.org/bugs/?67473>
Summary: ":|eval (exit 42)" does not return 42 Group: The GNU Bourne-Again SHell Submitter: pmhahn Submitted: Di 02 Sep 2025 16:38:44 GMT Category: None Severity: 3 - Normal Item Group: None Status: None Privacy: Public Assigned to: None Open/Closed: Open Discussion Lock: Any _______________________________________________________ Follow-up Comments: ------------------------------------------------------- Date: Di 02 Sep 2025 16:38:44 GMT By: Philipp Hahn <pmhahn> The GitLab runner showed a strange behavior, where the exit code of a shell command did not show up correctly and was always 1. GitLab fixed that by changing their code how they call `bash`. Details at https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27668 $ bash -e -c ': | bash -c "exit 42"'; echo $? 42 $ bash -e -c ': | eval "exit 42"'; echo $? 42 $ bash -e -c ' eval "bash -c \"exit 42\""'; echo $? 42 $ bash -e -c ' eval "(exit 42)" </dev/null'; echo $? 42 $ bash -e -c ': |(eval "(exit 42)" )'; echo $? 42 $ bash -e -c 'shopt -s lastpipe;: | eval "(exit 42)"';echo $? 42 $ bash -e -c ': | eval "bash -c \"exit 42\""'; echo $? 1 $ bash -e -c ': | eval "eval \"bash -c \\\"exit 42\\\"\""'; echo $? 1 $ bash -e -c ': | eval "(exit 42)"'; echo $? 1 Neither `dash` not `zsh` nor `ksh` show this behavior: $ dash -ec ':|eval "(exit 42)"'; echo $? 42 $ zsh -ec ':|eval "(exit 42)"'; echo $? 42 $ ksh -e -c ': | eval "(exit 42)"' 42 = Variants = * `errexit` is required to show the bug * using a pipe is required to show the bug * the `exit 42` must happen in another sub-process, either `(exit 42)` or `sh -c 'exit 42'` but not simply `exit 42` * `set -o pipefail` does not make any difference * `shopt -s lastpipe` fixes it = bisect = I have testes old version of bash and the bug seems to start after 4.0-rc1: $ podman run --rm docker.io/library/bash:3.0-alpine3.22 bash -e -c ': | eval "(exit 42)"'; echo $? 42 $ podman run --rm docker.io/library/bash:3.1-alpine3.22 bash -e -c ': | eval "(exit 42)"'; echo $? 42 $ podman run --rm docker.io/library/bash:3.2-alpine3.22 bash -e -c ': | eval "(exit 42)"'; echo $? 42 $ podman run --rm docker.io/library/bash:4.0-alpine3.22 bash -e -c ': | eval "(exit 42)"'; echo $? 1 $ podman run --rm docker.io/library/bash:5.0-alpine3.22 bash -e -c ': | eval "(exit 42)"'; echo $? 1 == git bisect == I have run `git bisect` to further shrink the set of changes, which shows the but to show up between `4.0-rc1` and `4.0`: $ cat ../bash.bisect #!/bin/sh git clean -fdx || exit 2 [ -x ./configure ] || autoconf || exit 2 ./configure || exit 125 make -j"$(nproc)" || exit 125 ./bash -e -c ': | eval "(exit 42)"' rv=$? echo "*** rv=$rv ***" git reset --hard [ $rv -eq 42 ] $ git bisect reset $ git bisect start bash-4.0 bash-3.2-alpha $ git bisect run ../bash.bisect … # bad: [89a92869e56aba4e4cab2d639c00a86f0545c862] Bash-4.0 patchlevel 38 # good: [e7f1978acfd2125b69bca36994882a1333607739] commit bash-20060706 snapshot git bisect start 'bash-4.0' 'bash-3.2-alpha' # skip: [7117c2d221b2aed4ede8600f6a36b7c1454b4f55] Imported from ../bash-2.05b.tar.gz. git bisect skip 7117c2d221b2aed4ede8600f6a36b7c1454b4f55 # good: [0628567a28f3510f506ae46cb9b24b73a6d2dc5d] Imported from ../bash-3.2.tar.gz. git bisect good 0628567a28f3510f506ae46cb9b24b73a6d2dc5d # skip: [3185942a5234e26ab13fa02f9c51d340cec514f8] Imported from ../bash-4.0-rc1.tar.gz. git bisect skip 3185942a5234e26ab13fa02f9c51d340cec514f8 # bad: [17345e5ad288f7543b77b23a25aa380eacc279f2] Imported from ../bash-4.0.tar.gz. git bisect bad 17345e5ad288f7543b77b23a25aa380eacc279f2 # skip: [f1be666c7d78939ad775078d290bec2758fa29a2] Imported from ../bash-3.2.48.tar.gz. git bisect skip f1be666c7d78939ad775078d290bec2758fa29a2 # only skipped commits left to test # possible first bad commit: [17345e5ad288f7543b77b23a25aa380eacc279f2] Imported from ../bash-4.0.tar.gz. # possible first bad commit: [3185942a5234e26ab13fa02f9c51d340cec514f8] Imported from ../bash-4.0-rc1.tar.gz. # possible first bad commit: [f1be666c7d78939ad775078d290bec2758fa29a2] Imported from ../bash-3.2.48.tar.gz. # good: [3185942a5234e26ab13fa02f9c51d340cec514f8] Imported from ../bash-4.0-rc1.tar.gz. git bisect good 3185942a5234e26ab13fa02f9c51d340cec514f8 # first bad commit: [17345e5ad288f7543b77b23a25aa380eacc279f2] Imported from ../bash-4.0.tar.gz. == manual cherry-picking == I have then re-applied parts of 4.0 on top of 4.0-rc1 until I found the relevant diff, which changes the behaviour of `execute_command_internal` and introduces the bug. Removing the following lines from `execute_command_internal()` in `execute_cmd.c` line 618-623 returns the old "good" behavior: - if (user_subshell && ignore_return == 0 && invert == 0 && exit_immediately_on_error && exec_result != EXECUTION_SUCCESS) - { - last_command_exit_value = exec_result; - run_pending_traps (); - jump_to_top_level (ERREXIT); - } I have also inserted some debug `fprintf()` to show the values at the point of execution: > 610> user_subshell=1 was_error_trap=0 ignore_return=0 invert=0 > exit_immediately_on_error=1 exec_result=42 > 2184> was_error_trap=0 ignore_return=0 invert=0 exit_immediately_on_error=1 > exec_result=42 _______________________________________________________ File Attachments: Reverted hunk Name: bash.diff Size: 2KiB <https://file.savannah.gnu.org/file/bash.diff?file_id=57614> AGPL NOTICE These attachments are served by Savane. You can download the corresponding source code of Savane at https://savannah.gnu.org/source/savane-4b8598a5d17f272f0bb6fd029e74d5ef737ff864.tar.gz _______________________________________________________ Reply to this item at: <https://savannah.gnu.org/bugs/?67473> _______________________________________________ Nachricht gesendet über Savannah https://savannah.gnu.org/
signature.asc
Description: PGP signature