From fc132ec52c5ecf88320e31166e0146f6d5fa852d Mon Sep 17 00:00:00 2001 From: pvincent Date: Thu, 9 Jul 2026 14:16:19 +0400 Subject: [PATCH] working FD3+4 --- _stderr | 3 +++ _stdout | 3 +++ bin/bash-strict | 25 ++++++++++++++----------- capture.sh | 31 +++++++++++++++++++++++++++++++ err | 4 ++++ stderr | 2 ++ stdout | 5 +++++ test5.sh | 4 ++-- test7.sh | 24 ++++++++++++++++++++++++ test8.sh | 34 ++++++++++++++++++++++++++++++++++ useless.sh | 10 ++++++++++ 11 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 _stderr create mode 100644 _stdout create mode 100755 capture.sh create mode 100644 err create mode 100644 stderr create mode 100644 stdout create mode 100755 test7.sh create mode 100755 test8.sh create mode 100755 useless.sh diff --git a/_stderr b/_stderr new file mode 100644 index 0000000..5a88af6 --- /dev/null +++ b/_stderr @@ -0,0 +1,3 @@ +DETECTED0: [This is stderr +./useless.sh: line 9: command_not_found: command not found] +ON_EXIT2 127 diff --git a/_stdout b/_stdout new file mode 100644 index 0000000..2720e46 --- /dev/null +++ b/_stdout @@ -0,0 +1,3 @@ +stdout A +stdout B +stdout C diff --git a/bin/bash-strict b/bin/bash-strict index 9e833d4..f746331 100755 --- a/bin/bash-strict +++ b/bin/bash-strict @@ -7,22 +7,23 @@ TMP_ERROR=$(mktemp -t "$(basename $0).XXXXXXXX" --tmpdir=/run/user/$(id -u)) # FUNCTIONS function exit { - code=${1:-0} + code=${1:-0} + echo + echo "ERROR #${code} due to:" stack_lines=("${BASH_LINENO[@]}") stack_functions=("${FUNCNAME[@]}") stack_sources=("${BASH_SOURCE[@]}") - tmp_error=${2:-/dev/null} - echo "ERROR #${code} due to:" - [[ -s $tmp_error ]] && cat $tmp_error && rm $tmp_error + tmp_error=${2:-/dev/null} + [[ -s $tmp_error ]] && cat $tmp_error && rm $tmp_error - echo "${stack_lines[@]}" - echo "${stack_functions[@]}" - echo "${stack_sources[@]}" + echo "${stack_lines[@]}" + echo "${stack_functions[@]}" + echo "${stack_sources[@]}" - trap - ERR TERM INT EXIT - builtin exit $code + trap - ERR TERM INT EXIT + builtin exit $code } # MAIN @@ -39,10 +40,12 @@ for i in "${original_args[@]}"; do quoted_args+=("${i@Q}"); done set -TEue -o pipefail export -f exit # echo PROCESS START + bash $opt_debug -c " set -TEue -o pipefail; trap 'exit \$? $TMP_ERROR' ERR TERM INT EXIT; source $command ${quoted_args[*]} -" 3>&1 1>&2 2>&3 | tee $TMP_ERROR -# echo PROCESS END succesfully + " 3>&1 1>&2 2>&3 | tee $TMP_ERROR +# TODO: delay the stacktrace message to output to stderr, idea: $TMP_ERROR + $TMP_STACK +# echo PROCESS END succesfully diff --git a/capture.sh b/capture.sh new file mode 100755 index 0000000..b98afae --- /dev/null +++ b/capture.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +{ + IFS=$'\n' read -r -d '' CAPTURED_STDERR + IFS=$'\n' read -r -d '' CAPTURED_STDOUT +} < <((printf '\0%s\0' "$(./useless.sh)" 1>&2) 2>&1) + +echo 'Here is the captured stdout:' +echo "${CAPTURED_STDOUT}" +echo + +echo 'And here is the captured stderr:' +echo "${CAPTURED_STDERR}" +echo + +# function launch { +# echo START +# bash -c "set -TEue -o pipefail; . ./useless.sh" +# echo END +# } +# +# set -TEue -o pipefail +# stderr=$(launch 3>&1 1>&2 2>&3 | tee /dev/stderr) +# >&2 echo "ERR: $stderr" +# # ( +# echo -n "ERR: " +# cat stderr +# ) >&2 +# >&2 echo 'here is the captured stderr:' +# >&2 echo "$(&2 echo diff --git a/err b/err new file mode 100644 index 0000000..fac535f --- /dev/null +++ b/err @@ -0,0 +1,4 @@ +This is stderr +./useless.sh: line 6: command_not_found: command not found +DETECTED [This is stderr +./useless.sh: line 6: command_not_found: command not found] diff --git a/stderr b/stderr new file mode 100644 index 0000000..0fdf0d9 --- /dev/null +++ b/stderr @@ -0,0 +1,2 @@ +This is stderr +./useless.sh: line 9: command_not_found: command not found diff --git a/stdout b/stdout new file mode 100644 index 0000000..407abd1 --- /dev/null +++ b/stdout @@ -0,0 +1,5 @@ +stdout A +stdout B +DETECTED3 [This is stderr +./useless.sh: line 6: nope: unbound variable] +ON_EXIT 1 diff --git a/test5.sh b/test5.sh index 8696fa7..b08c42e 100755 --- a/test5.sh +++ b/test5.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env -S -- bash-strict +#!./bin/bash-strict function f2 { # return 6 # exit 7 - unknown_command echo $nope + unknown_command } function f1 { diff --git a/test7.sh b/test7.sh new file mode 100755 index 0000000..c0409ff --- /dev/null +++ b/test7.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# function launch { +# exec 3>&1 1>&2 2>&3 ./useless.sh +# exec 3>&1 1>&2 2>&3 +# } + +function on_exit { + FAILURE=${1:-0} + [[ $FAILURE -gt 0 ]] && >&2 echo EXIT "$FAILURE" + trap - ERR TERM INT EXIT +} + +export -f on_exit +{ + myvar=$(bash -c " + set -TEue -o pipefail; + trap 'on_exit \$?' ERR TERM INT EXIT + . ./useless.sh + " 3>&1 1>&2 2>&3 | tee /dev/fd/4 | 3>&5 1>&2 2>&3 tee /dev/fd/5) +} 5>&1 4>&2 # magic trick: fd4 grabs stderr, then show up again on stderr + +echo ---------- +echo -e "stderr is:\n$myvar" diff --git a/test8.sh b/test8.sh new file mode 100755 index 0000000..268aa44 --- /dev/null +++ b/test8.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +function on_exit { + code=${1:-0} + # exec 3>&1 1>&2 2>&3 + # exec 1>&- 3>&- 2>&- + # exec 2> >(tee /dev/stderr) + trap - ERR TERM INT EXIT + # exec 2>&- + # exec 1> >(tee -ap /dev/stdout) 2>&- 3> >(tee -ap /dev/stderr) + # exec 2>&- 3>&- + # echo CLOSE OPEN PIPE suite + count=$(wc -c stderr | cut -d' ' -f1) + content=$(head -qc$count stderr) + >&4 echo "DETECTED0: [$content]" + >&4 echo ON_EXIT2 ${code} +} + +set -TEue -o pipefail +trap 'on_exit $?' ERR TERM INT EXIT +# exec 2>stderr 3> >(tee -ap /dev/stderr) +[[ -f stderr ]] && rm stderr +# exec 1> >(tee -p /dev/stdout) 2> >(tee stderr | tee -p /dev/stderr) 3> >(tee -p /dev/stderr) +# exec 2> >(tee stderr | tee -p /dev/stderr) # 3> >(tee -p /dev/stderr) +# exec 3> >(tee stderr | tee -p /dev/stderr) 2>&3 +# exec 3> >(tee stderr) 2>&3- +# exec 2> >(tee stderr) +exec 3>stderr 4>/dev/stderr +source ./useless.sh 2>&3 +exec 3>&- 4>&- +>&2 cat stderr +trap - ERR TERM INT EXIT +echo --END1 +>&2 echo --END2 diff --git a/useless.sh b/useless.sh new file mode 100755 index 0000000..5ea6cac --- /dev/null +++ b/useless.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "stdout A" +# echo toto >>/tmp/toto +>&2 echo "This is stderr" +echo "stdout B" +# $nope +echo "stdout C" +command_not_found +echo END of useless