0

I would like to write a script to access the state of an interactive bash shell, e.g. the script contains commands such as jobs, dirs, pushd and popd. So my strategy is to source the script in the shell when I run the script. But I often forget to source it and become confused for a while depending on how soon I realize it. Yes, I wrote the script myself.

So is there some best practice for forgetful people like me? For examples, is any of the following a good idea?

  1. How can we make running the script without typing source, for example, by modifying the script itself in some way?
  2. How can we make the script itself remind me of source it? For example,

    • rename the script file with a prefix Source_Me?
    • Or is there some command used inside the script to detect whether the script is invoked by source, and if not, output error message?
  3. Or should I try harder to remember which scripts need source?

    Thanks.

  • 1
    One fun way would be to eval a string that is the full content of the script. But no, just using source or . (dot) is the best (and only socially acceptable) way to execute another script file in the current shell's environment. – Kusalananda May 6 '18 at 13:10
4

If you source it, it doesn't require the execute bit.

Just don't make the script executable. If you then try to run it (without sourcing it), it just won't run (Permission denied), so no effect will happen. If it's in the PATH environment, a shell with completion (bash...) won't even try to autocomplete it with tab.

Now, you can just add in .profile, .bashrc or similar an alias:

alias myscript='. /path/to/myscript'

So from now, on typing myscript will source it and run it as intended. The shell will even autocomplete the alias.

  • Nice trick. And if the script is on a filesystem that doesn't support Unix permissions then you can make it harmless with a shebang like #!/bin/false. – Kamil Maciorowski May 6 '18 at 17:05
2

There are a couple of strategies you could use to print an error message if you run a script which is supposed to be sourced.

  • You could check whether $0 corresponds to your script’s name; if it does, it’s not being sourced, so print an error message an exit:

    if [ "${0##*/}" = "myscript" ]; then
        echo "Don't run me, source me" >&2
        exit 1
    fi
    

    (You would then need to take care to update that whenever your script is renamed...)

    A variant of this could be to check $0 against the contents of /etc/shells, if that’s an acceptable limitation (i.e. your shells scripts only ever run using shells listed there, which is by no means an obligation).

  • If your script is only intended for use in an interactive shell, you could check for that — if it’s run instead of being sourced, it won’t be in an interactive shell:

    case "$-" in
    *i*) ;;
    *) echo "Don't run me, source me" >&2; exit 1 ;;
    esac
    
1

seems you want a dot (.) like

#!/bin/sh
. includeMe.sh

but if you want just run all scripts from command like without any imperative "include"...

  1. it is impossible, because when you type a command it executes by system() call (such syntax)
  2. it is would be bad, because if in the script you run such way there is exit command it will bail out your current shell session

just try make script

script.sh:

#!/bin/bash
echo 'bye world';
exit

and run it as ./script.sh and compare with . ./script.sh

  • Dot is more or less the same as source in bash. If the issue is a problem with remembering to source a script, it will be the same with using dot. – Kusalananda May 6 '18 at 13:03
  • I explained more. In fact I almost never need to run scripts from command line with source or dot. if you need kepp some vars, use export when set them, for push and pop, there is no way. can you make a sample when you using it ? May be there is much better way ? – zb' May 6 '18 at 13:05
  • also, if you not use . how you would to tell the interpreter about difference between includes and binary system commands ? – zb' May 6 '18 at 13:13
1

Does it have to be a sourced script? Make it a function. So instead of

$ . something some args            # run it

have

$ cat define_something.sh
something() {
    commands here...
}
$ . define_something.sh            # load the function definition
$ something some args              # run the function

Or, if you want the actual commands to be in a sourced script, have a function to source it:

something() {
    . something "$@"
}

and then just run something foo bar instead of . something foo bar.

Of course functions hide the positional parameters of the main shell environment, so this won't work there. Perhaps an alias from something to . something if necessary. (Which was already suggested by A.B in their answer.)

1

How can we make the script itself remind me of source it?

Create a custom interpreter source_reminder and make it executable:

#!/bin/sh
printf "%s\n" "Source me!" >&2
exit 1

Then just use it in your executable script:

#!/path/to/your/source_reminder
foo
bar
whatever

Now if you run the script without sourcing, the reminder will do its job.

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.