I want to get a list of files and then read the results into an array where each array element corresponds to a file name. Is this possible?
-
1Yes, it is possible. Maybe not advisable if the names might contain arbitrary characters (spaces and newlines in the names cause grief), but it is doable. Which bit of the manual did you have difficulty understanding?Jonathan Leffler– Jonathan Leffler2012-06-11 13:53:07 +00:00Commented Jun 11, 2012 at 13:53
-
How is the list being defined? bash has arrays, but depending on how the list is generated, different techniquest are better than others. In any case, post also your own attempts to solve the problem.user1934428– user19344282021-10-26 11:57:31 +00:00Commented Oct 26, 2021 at 11:57
6 Answers
Don't use ls, it's not intended for this purpose. Use globbing.
shopt -s nullglob
array=(*)
array2=(file*)
array3=(dir/*)
The nullglob option causes the array to be empty if there are no matches.
4 Comments
grep, just include the string in your glob: array=(*.txt) or array=(*foo*)It is better and safer than using output of ls to use shell globbing pattern:
arr=( * )
echo ${#arr[@]} # will echo number of elements in array
echo "${arr[@]}" # will dump all elements of the array
6 Comments
ls is not necessary and should not be used for this purpose.ls output should be avoided.${arr[*]} and ${arr[@]} are the same.arr=( $(ls) ) line. Or if you want to leave it, please explicitly add a mention like don't do this, it's broken.In bash you can create an array of filenames with pathname expansion (globbing) like so:
#!/bin/bash
SOURCE_DIR=path/to/source
files=(
"$SOURCE_DIR"/*.tar.gz
"$SOURCE_DIR"/*.tgz
"$SOURCE_DIR"/**/*
)
The above will create an array called files and add to it N array elements, where each element in the array corresponds to an item in SOURCE_DIR ending in .tar.gz or .tgz, or any item in a subdirectory thereof with subdirectory recursion possible as Dennis points out in the comments.
You can then use printf to see the contents of the array including paths:
printf '%s\n' "${files[@]}" # i.e. path/to/source/filename.tar.gz
Or using parameter substitution to exclude the pathnames:
printf '%s\n' "${files[@]##*/}" # i.e. filename.tgz
6 Comments
shopt -s globstar in order to use recursive globbing (**).globstar. What does counting the characters in some directory names with echo /usr/**/ | wc -c output for you? On my Mac, in Sierra with Bash 3.2 or in Bash 4.4 with globstar off, it outputs 122. If I run Bash 4.4 with globstar on, I get 277904. The latter is clearly recursive and the former is not. BTW, shopt is defined, but I presume you mean globstar (in Bash 3.2, shopt -p globstar gives an error, in Bash 4.4 it shows whether it's set or unset).hash only works with external executables (try hash -t ls and help hash) and isn't going to show anything for shopt because it's a builtin or for globstar because it's an option rather than an executable. Try type -a shopt to show where shopt is coming from and shopt by itself to show the settings of all your options. Use echo "$BASH_VERSION" to show the version of your currently running shell (if it's Bash) and look at the output of ps -o tty,command to see if it's actually /bin/bash. Compare find /usr -type d | wc -l and echo /usr/**/ | tr -cd " " | wc -c ...Actually, ls isn't the way to go. Try this:
declare -a FILELIST
for f in *; do
#FILELIST[length_of_FILELIST + 1]=filename
FILELIST[${#FILELIST[@]}+1]=$(echo "$f");
done
To get a filename from the array use:
echo ${FILELIST[x]}
To get n filenames from the array starting from x use:
echo ${FILELIST[@]:x:n}
For a great tutorial on bash arrays, see: http://www.thegeekstuff.com/2010/06/bash-array-tutorial/
2 Comments
array+=(element). There's no reason to use $(echo "$f") just do the assignment directly. There's a missing closing curly brace on one of your echo statements.FILELIST = ( * )? (or, rather FILELIST += ( * )). Why on earth do you use $(echo "$f") instead of just "$f"?Try this,
path="" # could set to any absolute path
declare -a array=( "${path}"/* )
I'm assuming you'll pull out the unwanted stuff from the list later.
1 Comment
If you need a more specific file listing that cannot be returned through globbing, then you can use process substitution for the find command with a while loop delimited with null characters.
Example:
files=()
while IFS= read -r -d $'\0' f; do
files+=("$f")
done < <(find . -type f -name '*.dat' -size +1G -print0)