- FAIL
find ${settings_base} -type f -exec source {} \;
find: ‘source’: No such file or directory
- PASS
for f in $(find ${settings_base} -type f); do
    source "${f}"
done
I just want to know the reason for FAIL.
find ${settings_base} -type f -exec source {} \;
find: ‘source’: No such file or directory
for f in $(find ${settings_base} -type f); do
    source "${f}"
done
I just want to know the reason for FAIL.
 
    
     
    
    The -exec predicate of find runs a subprocess. But source cannot be run as a subprocess.
Shell built-ins like source (and cd, etc) modify the environment of the currently running shell instance. They don't exist as external commands (there is no /bin/source or /usr/bin/source etc) because they cannot work as external commands. Recall that a subprocess cannot modify the environment of its parent process, for reasons of security as well as for architectural reasons (see also e.g. Global environment variables in a shell script for background). So as a matter of fact, even if find could somehow execute source, it could only modify the environment of the find process, and then when find exits, any effects of that would be lost, and your currently running shell instance would be back where it was before you ran find.
The workaround you already discovered is the way to do it, though for complete robustness, you would also have to guard against whitespace or other shell metacharacters in the find output. https://mywiki.wooledge.org/BashFAQ/020 has a detailed discussion, but I won't repeat all the nuances and corner cases here; click through to the linked page for that.
while IFS= read -r -d '' f; do
  source "$f"
done < <(find $settings_base -type f -print0)
(Notice that -print0 is a GNU extension, and so not properly portable to various non-Linux platforms.)
Depending on what $settings_base contains, it should probably be double-quoted; if it contains multiple values, put them in an array and use "${settings_base_array[@]}". See also When to wrap quotes around a shell variable?
 
    
    If you are using Bash 4.3 (released in 2014) or later you can do what you want without using find:
#! /bin/bash -p
settings_base=$1
shopt -s dotglob globstar
for f in "$settings_base"/**; do
    [[ -f $f ]] && source -- "$f"
done
shopt -s ... enables some Bash settings that are required by the code:
dotglob enables globs to match files and directories that begin with ..  find processes such files by default.globstar enables the use of ** to match paths recursively through directory trees.  This option was introduced with Bash 4.0 (released in 2009) but it is dangerous to use in versions before 4.3 (released in 2014) because it follows symlinks.See glob - Greg's Wiki for more information about the globstar and dotglob settings.
