2

I have made a shell script to run as terminal command, but the cd commands inside it is not effective and hence I want to run it with source so that the cd commands take effect.

script name : "project.sh"

I added this file to /usr/local/bin, made it executable by chmod +x project.sh and it runs fine, but the cd command is not working.

I know it runs in a child process and hence at the end terminal returns back to the starting directory, rendering no effect of cd commands inside project.sh.

The solutions presented at Sol:1 do not work for me, because they asks me to run source <file>, which is not possible if I want to use it as Bash command.

Community
  • 1
  • 1
curiousMonkey
  • 677
  • 6
  • 15
  • Just define a *function* (for example in your `.bashrc`). – pynexj Oct 08 '15 at 01:28
  • 1
    If what you ask for were possible, it would be a nightmare (as an end-user): Right now, I can be assured that any command I run that isn't a shell builtin, function or alias will leave my shell's state (working directory, variables, etc) alone. You're asking for a way to violate that promise: It can't, **and shouldn't**, be done. Thus: It's impossible, and it's **intentionally** impossible, for good reasons. – Charles Duffy Oct 13 '15 at 20:49
  • ...there are commands that automatically edit a user's dotfiles, such as rvm, but it's a lot of work to do that in a way that's well-behaved (makes backups, etc) and adequately documented (so the user understands they need to restart open shells or re-source their dotfiles from them, etc). – Charles Duffy Oct 13 '15 at 20:54

2 Answers2

4

You use the source command:

source /usr/local/bin/project.sh

There's no way to make this happen automatically by typing the script name, that always runs the script in a subprocess. If you don't want to have to type this all out, you could create an alias in your .bashrc to simplify it:

alias project='source /usr/local/bin/project.sh'

Then typing project will be translated to that full command.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • [Barmar](http://stackoverflow.com/users/1491895/barmar) alias is a way but is there any way were i dont have to do that process ? Is it possible to set the script as source by default ? or some way i can add it to script so that script runs as sourced ? I want to make it like a package so that others can simply run it and all those parameters are set from the script itself. – curiousMonkey Oct 11 '15 at 00:50
  • 3
    You see where I said there's no way to make it happen automatically? I meant it. – Barmar Oct 11 '15 at 00:56
  • @ronpatel: The cumbersome-yet-only-feasible solution is to write an _installation mechanism_ that adds the sourcing command (whether via an alias or a function) to the user's shell profile / initialization file, complemented by logic in the script that makes it refuse to run when invoked _without_ sourcing; my (updated) answer has more. – mklement0 Oct 13 '15 at 20:40
2

Of course, source <file> is a Bash command - it uses the source builtin to run script <file> in the context of the current shell rather than in a child process, thus allowing commands in <file> to change the current shell's environment, such as in terms of the working directory (using cd).
Using source, or its alias ., is (ultimately) the only way to achieve that.

If your intent is not to have to invoke <script> explicitly with source, you have two options, both of which are best defined in your Bash profile, ~/.bash_profile (since you're on OS X; on Linux, use ~/.bashrc[1]):

I'll assume that your script is /path/to/foo, and that you want to invoke it sourced as just foo:

  • Option 1: Define an alias: alias foo='source "/path/to/foo"'
  • Option 2: Define a function: foo() { source "/path/to/foo"; }

Both aliases and functions execute in the current shell, allowing you to effectively hide the source call behind a single command; aliases are generally a little easier to define, but functions offer more flexibility.

By virtue of the alias / function being defined in your Bash profile, which itself is implicitly sourced, the commands in /path/to/foo will affect your interactive shells' environment.

Note: Either definition of foo will only be available in interactive shells (those that (automatically) source ~/.bash_profile). Additional steps would be needed to make foo work inside non-sourced scripts as well, but at that point you should ask yourself whether you're obscuring things by not making the fact that /path/to/foo is getting sourced explicit.


If you're writing a script that must be sourced for distribution to others:

  • Install the sourcing command in the user's shell profile / initialization file (as described above) on installation of your script.

    • If there is no installation process (and also to enable on-demand installation in general), implement a command-line option for your script such as i (--install) that performs this installation on demand.
      Preferably, also implement an uninstallation option.
  • Either way, build logic into the script so that it refuses to run when run without sourcing, and have the error message contain instructions on how to install sourcing.

A real-world implementation of the above - although more elaborate due to being multi-shell - is my typex utility; source code here.


[1] On OS X, Bash instances started by Terminal.app are login shells, which means that the only (user-specific) file that is automatically sourced on startup is ~/.bash_profile.
By contrast, on most Linux systems Bash instances are non-login shells, where only ~/.bashrc is automatically sourced.
While it is common practice to source ~/.bashrc from one's ~/.bash_profile, this has to be configured manually and therefore cannot be relied upon blindly.

Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775