2

When I declare an array in bash $ ARRAY=('ele1' ele2') I can append an element to it with $ ARRAY+=('ele3').

echo ${ARRAY[@]}
ele1 ele2 ele3

However, inside a script in a while loop I don't get it to work:

FOUNDFILES=$(ls -lA)
LINE_CNT=1
ARRAY=()

echo -e "$FOUNDFILES" | while read line
        do
                ARRAY+=("test")
                LINE_CNT=$((LINE_CNT+1))
        done

echo "${ARRAY[@]}"
echo $LINE_CNT

LINE_CNT variable delivers the amount of files that were found, but my array stays empty. What am I doing wrong?

h0ch5tr4355
  • 2,092
  • 4
  • 28
  • 51
  • 1
    You are printing `echo "${FILE_ARRAY[@]}"` instead of `echo "${ARRAY[@]}"`, maybe that lead you to believe that `ARRAY` is empty. – Socowi Dec 11 '19 at 15:52
  • 5
    Right hand side of a pipe runs in a subshell. Use `while ... done < <(echo -e "$FOUNDFILES")` isntead. – choroba Dec 11 '19 at 15:52
  • 1
    Also, capturing a list of files in a plain variable and `echo -e` are both unsafe ways to handle the file list. See [my answer here](https://stackoverflow.com/questions/1116992/capturing-output-of-find-print0-into-a-bash-array/1120952#1120952) for a safe way to do it. – Gordon Davisson Dec 11 '19 at 15:57
  • @Socowi That was a copy,paste mistake, edited it. Have confusing file names in my real example. – h0ch5tr4355 Dec 11 '19 at 16:12
  • @choroba This somehow creates an endless loop in my case, but I think this is the right track. `while ... \ndo \ndoSomething \ndone < <(echo -e "$FOUNDFILES")` – h0ch5tr4355 Dec 11 '19 at 16:18
  • h0ch5tr4355, `array` seems pointless since it just has "test" in it as many times as there were files found, and `line_count` since `line_count=${#foundfiles[@]};` I think you only need two lines from chepner's entry -, `shopt -s globstar; foundfiles=(./**/"$1");` – Paul Hodges Dec 11 '19 at 16:43
  • @PaulHodges Of course it is pointless. This script is just a placeholder. I have this while loop in many scripts and I wondered why I could increment the counter but not modify the array. – h0ch5tr4355 Dec 11 '19 at 16:48
  • Modified the question that `find` is not the deal since it is not used in my real script, just took it as placeholder for a command that creates multi line ouput – h0ch5tr4355 Dec 11 '19 at 16:51
  • Ah. The loop in in a subshell, so it can't modify the variable in the parent process. Fix it so you aren't appending the loop to a pipe. – Paul Hodges Dec 11 '19 at 16:52

1 Answers1

5

A few things:

  1. Don't assume that find outputs exactly one file name per line; that breaks in the presence of filenames containing newlines.

  2. Don't assume that newlines output by find are the only whitespace in the output.

  3. Don't use find at all when a glob will do.


shopt -s globstar
foundfiles=(./**/"$1")

declare -a array
line_cnt=1
for f in "${foundfiles[@]}"; do
    array+=(test)
    line_count=$((line_count + 1))
done

If your call to find is more complex than a glob can handle, and your version of find can output null-delimited file names, use

# -d for readarray was introduced in bash 4.4; earlier versions
# require something more complex; see Gordan Davidson's answer at
# https://stackoverflow.com/a/1120952/1126841
readarray -t -d $'\0' < <(find . ... -name "$1" -print0)

If your find does not support outputting null-delimited file names, rethink writing this in bash. (You might consider using zsh, which has a vastly richer set of glob features that can eliminate many cases where you would otherwise need find.)

chepner
  • 497,756
  • 71
  • 530
  • 681
  • How to use this? `ARRAY=(./**/"$1")` didnt create an array – h0ch5tr4355 Dec 11 '19 at 16:14
  • Furthermore there are no newlines in file names in my case. – h0ch5tr4355 Dec 11 '19 at 16:16
  • Ok now I managed it to work. This is indeed very helpful. However this rather advises not to use find, not answering my question of what I was doing wrong namely not considering the creation of a subshell. `FOUNDFILES=$(find . -name "$1")` was just a placeholder, because I have this while loop in many scripts and I wondered why I could increment the counter but not modify the array. – h0ch5tr4355 Dec 11 '19 at 16:45
  • Don't forget `shopt -s globstar;` - c.f. [bash's manual](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) – Paul Hodges Dec 11 '19 at 16:47
  • 1
    I find the assertion that one variable updates but the other does not dubious. – chepner Dec 11 '19 at 16:51
  • Will directly put this into my bashrc as it seems very helpful – h0ch5tr4355 Dec 11 '19 at 16:52