Search code examples
bashpipelinesubshell

Bash variable not decrementing in a pipeline


The variable x in the first example doesn't get decremented, while in the second example it works. Why?

Non working example:

#!/bin/bash

x=100
f() {
  echo $((x--)) | tr 0-9 A-J
  # this also wouldn't work: tr 0-9 A-J <<< $((x--))
  f
}
f

Working example:

#!/bin/bash

x=100
f() {
  echo $x | tr 0-9 A-J
  ((x--))
  # this also works: a=$((x--))
  f
}
f

I think it's related to subshells since I think that the individual commands in the pipeline are running in subshells.


Solution

  • It does decrement if you don't use a pipeline (and avoid a sub shell forking):

    x=10
    
    f() {
       if ((x)); then
          echo $((x--))
          f
       fi
    }
    

    Then call it as:

    f
    

    it will print:

    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
    

    Since decrement is happening inside the subshell hence current shell doesn't see the decremented value of x and goes in infinite recursion.


    EDIT: You can try this work around:

    x=10
    
    f() {
       if ((x)); then
          x=$(tr 0-9 A-J <<< $x >&2; echo $((--x)))
          f
       fi
    }
    
    f
    

    To get this output:

    BA
    J
    I
    H
    G
    F
    E
    D
    C
    B