Skip to main content
added comments to join_by function
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
  1. it's not that "printf can't handle the += operator", it's that you're using printf wrong, because bash doesn't expand arrays like you assume it does.

    printf "in while(printf): array=${array[@]}\n" is NOT expanded by bash as:

    printf "in while(printf):  a _b c d\n"`
    

    It is expanded as:

    printf "in while(printf):  a _b\n" "c" "d"
    

    The first element of the array is expanded inside the double-quoted string. The remainder are expanded as additional arguments outside of the string. Since you don't have any %s format strings ("conversion specifiers") for the args "c" and "d", they're not printed.

    To do what you want, try something like this:

    printf "in while(printf):  array="
    printf "%s " "${array[@]}"
    printf "\n"
    

    But note that there will be a trailing space in the output after the last element of the array. To avoid, that, and a better solution all-round is to use a proper join function to join the elements of the array into a single string. Unfortunately, bash doesn't have one built-in, but it's not too difficult to write one. e.g.

    function join_by {
      [ -z "$1" ] && return
      local d="$1"; shift;     # delimiter AKA separator
    
      [ -z "$1" ] && return
      printf "$1"; shift;      # first arg after delimiter
    
      printf "%s" "${@/#/$d}"; # remaining args, if any
    }
    
    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  $j\n"
    
  2. "cannot get results of += out of while loop".

    That's because the while loop is being run in a pipeline, which means that it's being run in a separate child shell. Child processes CAN NOT affect the environment of their parent process. This means that any changes you make to the array only happen within the child process, i.e. they're ephemeral and disappear when the child exits.

    You can avoid this by using Process Substition instead of a pipe to feed data into the while read loop. That way, the while read is being run in the main shell, not in a child shell. For example:

#!/bin/bash

function join_by {
  [ -z "$1" ] && return
  local d="$1"; shift;     # delimiter AKA separator

  [ -z "$1" ] && return
  printf "$1"; shift;      # first arg after delimiter

  printf "%s" "${@/#/$d}"; # remaining args, if any
} 


declare -a array=("a b")

while IFS= read -r line; do
    array+=($line)
    echo   "in while(echo):    array=${array[@]}"

    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  array=$j\n"

    echo; echo
done < <(printf "%s\n" c d)


echo   "out of while(echo):   array=${array[@]}"

j=$(join_by " " "${array[@]}")
printf "out of while(printf):  array=$j\n"
  1. it's not that "printf can't handle the += operator", it's that you're using printf wrong, because bash doesn't expand arrays like you assume it does.

    printf "in while(printf): array=${array[@]}\n" is NOT expanded by bash as:

    printf "in while(printf):  a _b c d\n"`
    

    It is expanded as:

    printf "in while(printf):  a _b\n" "c" "d"
    

    The first element of the array is expanded inside the double-quoted string. The remainder are expanded as additional arguments outside of the string. Since you don't have any %s format strings ("conversion specifiers") for the args "c" and "d", they're not printed.

    To do what you want, try something like this:

    printf "in while(printf):  array="
    printf "%s " "${array[@]}"
    printf "\n"
    

    But note that there will be a trailing space in the output after the last element of the array. To avoid, that, and a better solution all-round is to use a proper join function to join the elements of the array into a single string. Unfortunately, bash doesn't have one built-in, but it's not too difficult to write one. e.g.

    function join_by {
      [ -z "$1" ] && return
      local d="$1"; shift;
    
      [ -z "$1" ] && return
      printf "$1"; shift;
    
      printf "%s" "${@/#/$d}";
    }
    
    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  $j\n"
    
  2. "cannot get results of += out of while loop".

    That's because the while loop is being run in a pipeline, which means that it's being run in a separate child shell. Child processes CAN NOT affect the environment of their parent process. This means that any changes you make to the array only happen within the child process, i.e. they're ephemeral and disappear when the child exits.

    You can avoid this by using Process Substition instead of a pipe to feed data into the while read loop. That way, the while read is being run in the main shell, not in a child shell. For example:

#!/bin/bash

function join_by {
  [ -z "$1" ] && return
  local d="$1"; shift;

  [ -z "$1" ] && return
  printf "$1"; shift;

  printf "%s" "${@/#/$d}";
}

declare -a array=("a b")

while IFS= read -r line; do
    array+=($line)
    echo   "in while(echo):    array=${array[@]}"

    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  array=$j\n"

    echo; echo
done < <(printf "%s\n" c d)


echo   "out of while(echo):   array=${array[@]}"

j=$(join_by " " "${array[@]}")
printf "out of while(printf):  array=$j\n"
  1. it's not that "printf can't handle the += operator", it's that you're using printf wrong, because bash doesn't expand arrays like you assume it does.

    printf "in while(printf): array=${array[@]}\n" is NOT expanded by bash as:

    printf "in while(printf):  a _b c d\n"`
    

    It is expanded as:

    printf "in while(printf):  a _b\n" "c" "d"
    

    The first element of the array is expanded inside the double-quoted string. The remainder are expanded as additional arguments outside of the string. Since you don't have any %s format strings ("conversion specifiers") for the args "c" and "d", they're not printed.

    To do what you want, try something like this:

    printf "in while(printf):  array="
    printf "%s " "${array[@]}"
    printf "\n"
    

    But note that there will be a trailing space in the output after the last element of the array. To avoid, that, and a better solution all-round is to use a proper join function to join the elements of the array into a single string. Unfortunately, bash doesn't have one built-in, but it's not too difficult to write one. e.g.

    function join_by {
      [ -z "$1" ] && return
      local d="$1"; shift;     # delimiter AKA separator
    
      [ -z "$1" ] && return
      printf "$1"; shift;      # first arg after delimiter
    
      printf "%s" "${@/#/$d}"; # remaining args, if any
    }
    
    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  $j\n"
    
  2. "cannot get results of += out of while loop".

    That's because the while loop is being run in a pipeline, which means that it's being run in a separate child shell. Child processes CAN NOT affect the environment of their parent process. This means that any changes you make to the array only happen within the child process, i.e. they're ephemeral and disappear when the child exits.

    You can avoid this by using Process Substition instead of a pipe to feed data into the while read loop. That way, the while read is being run in the main shell, not in a child shell. For example:

#!/bin/bash

function join_by {
  [ -z "$1" ] && return
  local d="$1"; shift;     # delimiter AKA separator

  [ -z "$1" ] && return
  printf "$1"; shift;      # first arg after delimiter

  printf "%s" "${@/#/$d}"; # remaining args, if any
} 


declare -a array=("a b")

while IFS= read -r line; do
    array+=($line)
    echo   "in while(echo):    array=${array[@]}"

    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  array=$j\n"

    echo; echo
done < <(printf "%s\n" c d)


echo   "out of while(echo):   array=${array[@]}"

j=$(join_by " " "${array[@]}")
printf "out of while(printf):  array=$j\n"
added 146 characters in body
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
a=array=("a b")
typeset -p array

readarray -t btmp_array < <(printf "%s\n" c d)
a+=typeset -p tmp_array

array+=("${b[@]tmp_array[@]}")
typeset -p aarray
declare -a a=array=([0]="a b")
declare -a tmp_array=([0]="c" [1]="d")
declare -a array=([0]="a b" [1]="c" [2]="d")
a=("a b")
readarray -t b < <(printf "%s\n" c d)
a+=("${b[@]}")
typeset -p a
declare -a a=([0]="a b" [1]="c" [2]="d")
array=("a b")
typeset -p array

readarray -t tmp_array < <(printf "%s\n" c d)
typeset -p tmp_array

array+=("${tmp_array[@]}")
typeset -p array
declare -a array=([0]="a b")
declare -a tmp_array=([0]="c" [1]="d")
declare -a array=([0]="a b" [1]="c" [2]="d")
deleted 8 characters in body
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
#!/bin/bash

function join_by {
  [ -z "$1" ] && return
  local d="$1"; shift;

  [ -z "$1" ] && return
  printf "$1"; shift;

  printf "%s" "${@/#/$d}";
}

declare -a array=("a b")

while IFS= read -r line; do
    array+=($line)
    echo   "in while(echo):    array=${array[@]}"

    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  array=$j\n"

    echo; echo
done < <(printf "c\nd" |"%s\n" xargsc -n1d)


echo   "out of while(echo):   array=${array[@]}"

j=$(join_by " " "${array[@]}")
printf "out of while(printf):  array=$j\n"
#!/bin/bash

function join_by {
  [ -z "$1" ] && return
  local d="$1"; shift;

  [ -z "$1" ] && return
  printf "$1"; shift;

  printf "%s" "${@/#/$d}";
}

declare -a array=("a b")

while IFS= read -r line; do
    array+=($line)
    echo   "in while(echo):    array=${array[@]}"

    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  array=$j\n"

    echo; echo
done < <(printf "c\nd" | xargs -n1)


echo   "out of while(echo):   array=${array[@]}"

j=$(join_by " " "${array[@]}")
printf "out of while(printf):  array=$j\n"
#!/bin/bash

function join_by {
  [ -z "$1" ] && return
  local d="$1"; shift;

  [ -z "$1" ] && return
  printf "$1"; shift;

  printf "%s" "${@/#/$d}";
}

declare -a array=("a b")

while IFS= read -r line; do
    array+=($line)
    echo   "in while(echo):    array=${array[@]}"

    j=$(join_by " " "${array[@]}")
    printf "in while(printf):  array=$j\n"

    echo; echo
done < <(printf "%s\n" c d)


echo   "out of while(echo):   array=${array[@]}"

j=$(join_by " " "${array[@]}")
printf "out of while(printf):  array=$j\n"
added readarray example
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
Loading
renamed join to join_by to avoid conflict with /usr/bin/join
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
Loading
renamed join to join_by to avoid conflict with /usr/bin/join
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
Loading
Source Link
cas
  • 83.9k
  • 8
  • 136
  • 205
Loading