Home > Net >  Iterating over an array of dir names containing spaces has a different behaviour if the array is the
Iterating over an array of dir names containing spaces has a different behaviour if the array is the

Time:12-28

In bash, I think I know how to iterate over an array of strings containing spaces:

~ $ arr=( "/home/user/Images/three parts dirname/ccc.png" "/home/user/Images/three parts dirname/bbb.png" "/home/user/Images/three parts dirname/aaa.png" )
~ $ for i in "${arr[@]}"; do echo "$i"; done

/home/user/Images/three parts dirname/ccc.png
/home/user/Images/three parts dirname/bbb.png
/home/user/Images/three parts dirname/aaa.png

I want to do something similar in a script that uses find. So I'm doing...

~ $ d="/home/user/Images/three parts name"
~ $ arr=( $(find -L "$d" -maxdepth 1 -iname '*.jp*g' -o -iname '*.png' -printf '"%p" ') )

...because the command find -L "$d" -maxdepth 1 -iname '*.jp*g' -o -iname '*.png' -printf '"%p" ' gives exacltly:

"/home/user/Images/three part name/ccc.png" "/home/user/Images/three part name/bbb.png" "/home/user/Images/three part name/aaa.png" 

The problem is that in this case I have the result below:

~ $ for i in "${arr[@]}"; do echo "$i"; done

"/home/user/Images/three
parts
name/ccc.png"
"/home/user/Images/three
parts
name/bbb.png"
"/home/user/Images/three
parts
name/aaa.png"

So I cannot iterate over those files successfully. I know I can avoid spaces in dirs and files names (and I barely have any) but I wanted the script to work anyway, just in case.

What I mean is: why If I use find to define the array, the 1st item, i.e., is

"/home/user/Images/three

and not

/home/user/Images/three parts dirname/ccc.png ?

It seems that with find the spaces in the dir names are "hard coded" and/or the double quotes are part of the strings (separated by spaces) themselves.

EDIT here for clarity:
I accepted the answer using find. It has to be corrected adding escaped brackets though, according to stackoverflow.com/a/6957310/1865860. I also wanted to further process the find results, so I had to rely on stackoverflow.com/a/11789688/1865860.

The final output looks like:

readarray -d $'\0' TOT_IMAGES < <(find -L "$i" -maxdepth 1 \( -iname '*.jp*g' -o -iname '*.png' -o -iname '*.gif' -o -iname '*.tif*' \) -print0);
IFS=$'\n';
SORTED_IMAGES=($(sort <<<"${TOT_IMAGES[*]}")); 
TOP4_IMAGES=($(head -n4 <<<"${SORTED_IMAGES[*]}"));
unset IFS

CodePudding user response:

You could do this with pure bash, without using the find:

shopt -s nullglob nocaseglob
arr=("$d"/{*.png,*.jp*g})

CodePudding user response:

You can use find with print0 option to have ASCII NUL character separator (instead of NL) and then use readarray

readarray -d $'\0' arr < <(find -L "$d" -maxdepth 1 -iname '*.jp*g' -o -iname '*.png' -print0)

so you don't have to worry about the spaces.

  • Related