4

I have shell scripts in my ~/Shell directory that I want to be run whenever Bash is started up as my usual user account. So what I have done is added the following to ~/.bashrc:

for i in `find ~/Shell/ -name "*.sh"`
do
    sh $i
done

but, for whatever reason the functions contained in files with the file extension .sh in my ~/Shell directory are not automatically loaded. For example, I have a function called abash in my ~/Shell/bash.sh file and running abash from a new user terminal gave an error stating that the command was not found.

I know I can just manually list all the files in my ~/Shell directory with a dot before them to get them executed at Bash startup time. For example, I used to have this in my ~/.bashrc file:

. ~/Shell/bash.sh
. ~/Shell/cd.sh
. ~/Shell/emerge.sh
...

and it worked fine, but I would rather a for loop to do this, as it would mean if I add any new shell scripts to ~/Shell I do not have to worry about adding them to ~/.bashrc.

I have also now tried:

for i in `find -name "~/Shell/*.sh"`
do
        sh $i
done

and:

for i in "~/Shell/*.sh"
do
        sh $i
done

and:

for i in `find -name '~/Shell/*.sh'`
do
        sh $i
done

with no success.

  • for i in ~/Shell/*.sh do; . $i; done – Jeff Schaller Oct 31 '15 at 2:15
  • Just gave that a try and it didn't work. – BH2017 Oct 31 '15 at 2:18
  • Don't execute it with sh; dot it in with . – Jeff Schaller Oct 31 '15 at 2:24
  • Yes, although I decided to drop the i in -iname and use . instead of source. I did eventually drop his answer altogether and use Jeff's answer as it is simpler, but his answer worked with the adjustments I mentioned, at least. BinaryZebra (BZ)'s answer I find overly complex (I'm a programming noob tbh) and since I last saw it has become more complex and confusing. I think I'll accept your answer, now I think of it. – BH2017 Oct 31 '15 at 7:26
  • Glad you got a solution (and I see RobertL wrote it up); your answer had enough direction in it to solve your actual problem instead of focusing on the particular syntax you had been trying. BinaryZebra has some advanced shell usage that you can also learn from at your leisure. – Jeff Schaller Oct 31 '15 at 11:43
3

Put this in your .bashrc:

for rc in ~/Shell/*.sh
do
    . "$rc"
done

And you're off to the races!

A couple of notes:

The bash (and zsh etc) source command, while readable, is not universal and does not exist in dash, the most posixly correct shell I know. As it stands, this same code can be used to load code into almost any bourne-shell derivative.

The traditional naming convention for files to be directly sourced into the shell is to use a suffix of rc or .rc (as in .bashrc). rc stands for "run commands". The .sh extension is usually used for executable script programs. (These are only conventions -- not rules.)

  • The ~/Shell/* will not include sub-directories (if used) under the directory Shell. It is similar but not equivalent to find. – user79743 Oct 31 '15 at 3:11
  • You are correct. By design. find is overkill for this. None of the OP examples contained subdirectories. Plus, this answer helped you fix yours! You should thank me! – RobertL Oct 31 '15 at 3:40
  • Find is indeed very powerful, but that is what the user used and we should have an answer which is similar or note the differences (if any exist). That is the intent of my comment, to note the difference. – user79743 Oct 31 '15 at 3:52
  • If that makes you feel happy, then: Many, many thanks to you!! – user79743 Oct 31 '15 at 3:52
1

Several issues:

  • To work, the tilde needs to be un-quoted. Use $HOME, instead.
  • To work, the * also needs to be un-quoted. Not needed for find.
  • There should NOT be / in file names ("~/Shell/") for find.
  • As files need to loaded and not executed, sh should be changed to source.

Try this:

for i in $(find "$HOME/Shell/" -iname "*.sh")
do 
    source "$i"
done

EDIT:

The option above could not process file names with spaces, newlines or problematic characters. To solve that we may use the full capacities of find, as this:

while IFS= read -r -d $'\0' file; do
    source "$file"
done < <(find "$HOME/Shell/" -name "*.sh" -type f -print0)

NOTE:

A solution with a simple asterisk * will not include sub-directories.

A complete solution using asterisks that will include sub-directories in the same manner as find just looks too complex:

resetextglob=$(shopt -p extglob)     # store value of extglob
resetnullglob=$(shopt -p nullglob)   # store value of nullglob

shopt +s extglob nullglob
for i in $HOME/Shell/*.sh $HOME/Shell/**/*.sh
do
    source "$i"
done

$resetextglob                        # reset extglob to original value
$resetnullglob                       # reset nullglob to original value
  • Just tried that, no errors came out but it didn't work as running abash from a new terminal tab after adding this to ~/.bashrc returned: bash: abash: command not found – BH2017 Oct 31 '15 at 2:48
  • No, you mustn't have read my question properly -- abash is a function specified in ~/Shell/bash.sh – BH2017 Oct 31 '15 at 2:59
  • Changed answer, changed bash (execution) to source (loading) of files. – user79743 Oct 31 '15 at 3:02
  • This one won't work if script name contain space, tab, or newline, and required find implementation supported -iname. – cuonglm Oct 31 '15 at 3:06
  • @BinaryZebra: So please add a note. It's better to assume the OP only want one level, or at least using **/*.sh with extglob enable. – cuonglm Oct 31 '15 at 3:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.