Folder comparison
I have two folders with similar subfolder structures, which I would like to compare. For example:
A
├── child-1
├── child-2
├── child-3
├── child-4
├── child-5
and
B
├── child-1-some-text
├── child-2-more-text
├── child-3-nothing
├── child-6-random-text
├── child-7-more-random-text
I would like to list all those subfolders from A
which are prefix for a subfolder in B
and list corresponding subfolders from B
as well. The expected output is
child-1 -- child-1-some-text
child-2 -- child-2-more-text
child-3 -- child-3-nothing
A secondary requirement: If multiple matches in B
, then it should give an error / warning.
My solution:
cd A
for f in `ls -d */`;
do
cd B;
new_dirs=(`ls -1d $f*`);
cd -;
if [ ${#new_dirs[@]} -eq 0 ]
then
## DO_Nothing
continue;
elif [ ${#new_dirs[@]} -gt 1 ]
then
echo "Multiple matches to $f";
continue;
else
echo "Unique Match found to $f -- ${new_dirs[0]}";
continue;
fi;
done
Problem:
For those values of $f
, which have no corresponding subfolders in B
, the array construction is giving me an error. e.g.:
ls: cannot access 'child-4*': No such file or directory
Question
- How to get rid of these errors?
- Is there better way to achieve the goal(s) then the one in my code?
Thanks in advance!
Solution 1:
The better way
Don't parse ls
; use globs instead. In fact you're already using globs, just wrapping them in ls
, which is pointless. You just need nullglob
turned on for when there are no matches.
Also avoiding cd
simplifies things.
#!/bin/bash
shopt -s nullglob
dir1=A
dir2=B
for dir in "$dir1"/*/; do
basename="$(basename -- "$dir")"
dirs_match=( "$dir2/$basename"*/ )
case ${#dirs_match[@]} in
0)
;;
1)
echo "Unique match for $dir: ${dirs_match[*]}"
;;
*)
echo "Multiple matches for $dir: ${dirs_match[*]}" >&2
;;
esac
done
Output:
Unique match for A/child-1/: B/child-1-some-text/
Unique match for A/child-2/: B/child-2-more-text/
Multiple matches for A/child-3/: B/child-3-nothing/ B/child-3-something/
I added B/child-3-something
to test the secondary requirement. This creates the directory structure for testing:
mkdir -p A/child-{1..5} B/child-{1-some-text,2-more-text,3-nothing,3-something,6-random-text,7-more-random-text}
By the way, ShellCheck is very useful for finding problems in shell scripts.
Solution 2:
Calling ls
on a non existent folder throws the error message that you encountered. The easy way is to just ignore this by replacing line 5 in your script with this: new_dirs=(`ls -1d $f* 2> /dev/null`);
.