Reputation: 4517
I have an array containing a list of file paths within different levels of directories. I want to filter that array to only files,meaning those that end with an /
should remain in the array. All paths are no real paths on my file system I could check the type using OS tools for directories/files.
I have an array with following strings:
file-a
dir/
dir/file-b
dir/dir-2/
dir/dir-2/file-c
And want to filter it to the following strings (no directories):
file-a
dir/file-b
dir/dir-2/file-c
I tried it with the following but that removes all paths including a /
, resulting in file-a
being the only remain.
FILES_ARRAY=( ${FILES_ARRAY[@]//*\/} )
I tried to add an $
(as common in regex syntax) to denote the end which does not remove anything from the array.
FILES_ARRAY=( ${FILES_ARRAY[@]//*\$/} )
Upvotes: 1
Views: 277
Reputation: 34484
Two different solutions depending on what filter
means:
1 - remove the directory entries from the array:
declare -a FILES_ARRAY=([0]="file-a" [1]="dir/" [2]="dir/file-b" [3]="dir/dir-2/" [4]="dir/dir-2/file-c")
echo "++++++++++++++ array - before"
printf "%s\n" ${FILES_ARRAY[@]}
for i in ${!FILES_ARRAY[@]}
do
[[ "${FILES_ARRAY[${i}]}" == */ ]] && unset FILES_ARRAY[${i}] # remove directory from array
done
echo "++++++++++++++ array - after"
printf "%s\n" ${FILES_ARRAY[@]}
This generates:
++++++++++++++ array - before
file-a
dir/
dir/file-b
dir/dir-2/
dir/dir-2/file-c
++++++++++++++ array - after
file-a
dir/file-b
dir/dir-2/file-c
2 - leave the directory entries in the array but don't display them; we can re-use the above code with a change to the body of the for
loop:
declare -a FILES_ARRAY=([0]="file-a" [1]="dir/" [2]="dir/file-b" [3]="dir/dir-2/" [4]="dir/dir-2/file-c")
echo "++++++++++++++ array"
printf "%s\n" ${FILES_ARRAY[@]}
echo "++++++++++++++ display"
for i in ${!FILES_ARRAY[@]}
do
[[ "${FILES_ARRAY[${i}]}" != */ ]] && echo "${FILES_ARRAY[${i}]}" # only display non-directory entries
done
This also generates:
++++++++++++++ array
file-a
dir/
dir/file-b
dir/dir-2/
dir/dir-2/file-c
++++++++++++++ display
file-a
dir/file-b
dir/dir-2/file-c
Upvotes: 2
Reputation: 7277
This awk
command may help, but it'll fail with spaces in paths
new_arr=($(awk '{for (i=1; i<=NF; i++) if ($i !~ /\/$/) {print $i}}' <<< "${arr[@]}"))
Upvotes: 0
Reputation: 626896
You can use
FILES_ARRAY=(file-a dir/ dir/file-b dir/dir-2/ dir/dir-2/file-c)
NEW_FILES_ARRAY=()
for (( i=0; i<${#FILES_ARRAY[@]}; i++ )); do
if ! [[ "${FILES_ARRAY[$i]}" == */ ]]; then
NEW_FILES_ARRAY+=("${FILES_ARRAY[$i]}");
fi
done
FILES_ARRAY=("${NEW_FILES_ARRAY[@]}")
printf '%s\n' ${FILES_ARRAY[@]}
Output:
file-a
dir/file-b
dir/dir-2/file-c
See an online Bash demo.
NOTES:
for (( i=0; i<${#FILES_ARRAY[@]}; i++ )); do ... done
iterates over the FILES_ARRAY
length, i
is assigned indices of the array elementsif ! [[ "${FILES_ARRAY[$i]}" == */ ]]
checks if the current element does not end with /
(*
matches any amount of any chars and /
matches /
, the glob pattern always requires entire string match, no need for $
) andNEW_FILES_ARRAY+=("${FILES_ARRAY[$i]}");
adds the current item to the NEW_FILES_ARRAY
array.FILES_ARRAY=("${NEW_FILES_ARRAY[@]}")
reassigns the original variable so that it holds the filtered elementsUpvotes: 0