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"



Reply via email to