0

Please be aware that this is just a pseudo code to illustrate the problem:

Directory structure:

# tree
.
├── dir1
│   ├── d1_file1
│   ├── d1_file2
│   └── d1_file3
└── dir2
    ├── d2_file1
    ├── d2_file2
    └── d2_file3

2 directories, 6 files

Script:

#!/bin/bash

list=(
dir1
dir2
)

for dir in ${list[@]}; do
    ls -1 ./tmp/${dir} | while read -a file; do
    files+=(${file})
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
    done
done

Current output:

# bash tmp.sh 
dir1 has 1 files(s)
dir1 files: d1_file1
dir1 has 2 files(s)
dir1 files: d1_file1 d1_file2
dir1 has 3 files(s)
dir1 files: d1_file1 d1_file2 d1_file3
dir2 has 1 files(s)
dir2 files: d2_file1
dir2 has 2 files(s)
dir2 files: d2_file1 d2_file2
dir2 has 3 files(s)
dir2 files: d2_file1 d2_file2 d2_file3

Desired outcome:

# bash tmp.sh
dir1 has 3 files(s)
dir1 files: d1_file1 d1_file2 d1_file3
dir2 has 3 files(s)
dir2 files: d2_file1 d2_file2 d2_file3

Basically I would like to build a separate array for each item in the for loop that stores values from the ls command.

2 Answers 2

2

In order to only output the count once per directory, you need to move the printing out of the while read loop. However, I would suggest removing it entirely. Try something like this:

#!/bin/bash

list=(
dir1
dir2
)

for dir in "${list[@]}"; do
    files=( ./tmp/"$dir"/* )
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
done

This uses a glob to populate the files array with the contents of each directory.

In general, parsing ls should be avoided.

Sign up to request clarification or add additional context in comments.

5 Comments

It looks like files variable is empty when I move the print out of the while loop?
@HTF that's because piping creates a sub shell, so variables declared inside are lost. There are ways around this but I would suggest avoiding the problem entirely and using the approach I have suggested in my answer.
Thanks, please note that this is just a pseudo code, basically I didn't want to paste the whole script so I just used ls for example. The original script is used to iterate through AWS S3 buckets.
@HTF fair enough, in that case one option would be to pipe the other aws command to a block { while read; do ... done; echo ... }
Thanks, reference is someone else will need this info: mywiki.wooledge.org/BashFAQ/024
1

You can simply use pathname expansion (globbing) to capture all the files in your subdirectories:

list=(
 dir1
 dir2
)

for dir in "${list[@]}"; do
    files=( ./tmp/"${dir}"/* )  # collect all matches in array
    echo "${dir} has ${#files[@]} files(s)"
    echo "${dir} files: ${files[@]}"
done

Note how I've double-quoted ${list[@]} and ${dir} to protect their values from shell expansions.
By contrast, the * must be unquoted so as to trigger pathname expansion.

2 Comments

Exactly. At least our stories check out!
@TomFenech: :) ++ for providing a better explanation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.