简体   繁体   中英

How to remove all strings from array that end with a specific character

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[@]//*\$/} )

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

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 for (( i=0; i<${#FILES_ARRAY[@]}; i++ )); do... done iterates over the FILES_ARRAY length, i is assigned indices of the array elements
  • if ! [[ "${FILES_ARRAY[$i]}" == */ ]] if ! [[ "${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 $ ) and
  • NEW_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 elements

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[@]}"))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM