Package: bash
Version:|5.3.3(1)-release |
*Architecture:* Debian GNU/Linux forky/sid |x86_64 Linux|
|In the end of email exists both scripts with their output plus another
example|
Possible bug in Bash: variables from pipeline inside |while read| loop
intermittently empty. I have encountered inconsistent behavior in Bash
when reading lines from a file using |while read| combined with command
substitution (|$(...)|) or pipelines. In some cases, variables that are
explicitly set in the loop appear empty when later used.
SCRIPT 1 *Description:*
When using a custom |PS4| that references |${FUNCNAME[0]}| in a script
with |set -x|, Bash triggers repeated errors: |bash: FUNCNAME[0]:
variable withoutconnection|
even though the script logic executes correctly. This occurs when
commands are executed in a subshell (e.g., via a pipeline), causing Bash
to attempt to expand |${FUNCNAME[0]}| in contexts where it is undefined.
*Observed behavior:*
*
Bash prints the |variable without connection| error for every
command in the loop, even though the commands execute successfully.
T his seems related to the use of |${FUNCNAME[0]}| inside |PS4|
combined with pipelines or command substitutions (||| and |$()|),
which run in subshells.
SCRIPT 2 (correct) description:
*Observed behavior:*
*
No errors are produced.
*
Script behaves as expected, and variable |value| is retained correctly.
*Impact:*
*
Using |${FUNCNAME[0]}| in |PS4| breaks tracing for scripts that use
pipelines or command substitutions.
*
Could affect debugging of scripts that rely on |set -x| for logging
or tracing.
*Additional Information:*
*
Removing |${FUNCNAME[0]}| from |PS4| resolves the issue.
*
Bash documentation implies |${FUNCNAME}| should be valid, but it
seems not in subshells created by pipelines.
*Suggested action:*
*
Investigate why |${FUNCNAME[0]}| is undefined in subshells used for
pipelines, and consider either documenting this limitation or fixing
the tracing expansion logic in Bash.
# ======script 1 (problematic) & its output=========
#!/usr/bin/env bash
# Custom PS4 referencing FUNCNAME[0] (this triggers the bug)
PS4='+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} '
set -x
while IFS= read -r LINE; do
# reset variable
value=""
# simulate pipeline extraction
value=$(echo "$LINE" | grep -o "num=[0-9]\+" | sed "s/num=//")
echo "Before printf:"
declare -p value
# try to printf
printf "%05d\n" "$value"
echo "After printf:"
declare -p value
done <<< "num=123"
############## RESULTS
"bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} IFS=
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} read -r LINE
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} value=
bash: FUNCNAME[0]: variable without connection
++ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} echo num=123
bash: FUNCNAME[0]: variable without connection
++ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} grep -o 'num=[0-9]\+'
bash: FUNCNAME[0]: variable without connection
++ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} sed s/num=//
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} value=123
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} echo 'Before printf:'
Before printf:
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} declare -p value
declare -- value="123"
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} printf '%05d\n' 123
00123
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} echo 'After printf:'
After printf:
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} declare -p value
declare -- value="123"
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} IFS=
bash: FUNCNAME[0]: variable without connection
+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} read -r LINE
bash: FUNCNAME[0]: variable without connection
++ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} _pyenv_virtualenv_hook
++ 0:2:_pyenv_virtualenv_hook local ret=0
++ 0:3:_pyenv_virtualenv_hook '[' -n '' ']'
+++ 1:1:_pyenv_virtualenv_hook pyenv sh-activate --quiet
+++ 1:2:pyenv local command=sh-activate
+++ 1:3:pyenv '[' 2 -gt 0 ']'
+++ 1:3:pyenv shift
+++ 1:163:pyenv case "$command" in
+++ 1:9:pyenv command pyenv sh-activate --quiet
+++ 1:1:_pyenv_virtualenv_hook true
++ 0:6:_pyenv_virtualenv_hook eval false
+++ 0:1:_pyenv_virtualenv_hook false
++ 0:6:_pyenv_virtualenv_hook true
++ 0:8:_pyenv_virtualenv_hook return 0
"
#======2nd scipt working correctly ====
#!/usr/bin/env bash
# Safe PS4 (no FUNCNAME[0])
PS4='+ $BASH_SUBSHELL:$LINENO '
set -x
while IFS= read -r LINE; do
# reset variable
value=""
# simulate pipeline extraction
value=$(echo "$LINE" | grep -o "num=[0-9]\+" | sed "s/num=//")
echo "Before printf:"
declare -p value
# try to printf
printf "%05d\n" "$value"
echo "After printf:"
declare -p value
done <<< "num=123"
+ 0:7 IFS=
+ 0:7 read -r LINE
+ 0:9 value=
++ 1:1 echo num=123
++ 1:1 grep -o 'num=[0-9]\+'
++ 1:1 sed s/num=//
+ 0:12 value=123
+ 0:14 echo 'Before printf:'
Before printf:
+ 0:15 declare -p value
declare -- value="123"
+ 0:18 printf '%05d\n' 123
00123
+ 0:20 echo 'After printf:'
After printf:
+ 0:21 declare -p value
declare -- value="123"
+ 0:7 IFS=
+ 0:7 read -r LINE
++ 0:23 _pyenv_virtualenv_hook
++ 0:2 local ret=0
++ 0:3 '[' -n '' ']'
+++ 1:1 pyenv sh-activate --quiet
+++ 1:2 local command=sh-activate
+++ 1:3 '[' 2 -gt 0 ']'
+++ 1:3 shift
+++ 1:163 case "$command" in
+++ 1:9 command pyenv sh-activate --quiet
+++ 1:1 true
++ 0:6 eval false
+++ 0:1 false
++ 0:6 true
++ 0:8 return 0
# =========================
# /*Another incorrect script - vars are not set correctly while all
other reasons excluded eg. wrong chars. */
while IFS='' read -r LINE; do
est_frame=$(echo "$LINE" | grep -o "estimated_frame=[0-9]\+" | sed
"s/estimated_frame=//")
time_pos=$(echo "$LINE" | grep -o "time_pos=[0-9.]\+" | sed
"s/time_pos=//")done < frm.txt
declare -p est_frame
declare -p time_pos
PS4='+ $BASH_SUBSHELL:$LINENO:${FUNCNAME[0]} '
set -x
echo "$LINE" | grep -o "estimated_frame=[0-9]\+" | sed
"s/estimated_frame=//"
set +x
declare -p est_frame
declare -p time_pos
frame_padded=$(printf "%05d" "$est_frame");
int_part=${time_pos%.*};
frac_part=${time_pos#*.};
int_part_padded=$(printf "%05d" "$int_part");
timepos_formatted="${int_part_padded}.${frac_part}";
out_file="$output_dir/${vid_scr}_${frame_padded}_${timepos_formatted}.bmp";
ffmpeg -loglevel error -y -ss "$timepos_formatted" -i "$vid_scr"
-frames:v 1 -q:v 1 "$out_file" > /dev/null 2>&1
done < frm.txt
frm.txt contains lines like
"time_pos=78.501222222222, estimated_frame=2355"