I knew that commands in a makefile get executed in a new instance of shell (one new instance per each command). But I did not know that my .cshrc.local (in case of csh/tcsh) is sourced again for each of that shell instance!
How can I tell make to not do that? I want each shell instance created from inside make to simply get the current shell's environment (the one from where I am running make) and not source my .cshrc.local
Hope the question makes sense.
My bad. I ignored the fact that my command was a shell script (with a shebang /bin/tcsh). I looked at tcsh help, and found that I should pass -f argument if I want to skip sourcing the startup file. Added that to my shell script and it does what I want.
So, I was mistaken that make commands source startup file while creating a new shell instance. They do not.
Related
I am learning the basics of bash and linux. To execute a script, I could type...
bash script1
or
source script1
or
./script1
The first two will run without chmod u+x and the last one requires it.
From my understanding, bash tries to run things in a subshell so it doesn't mess things up. When I add bash before the filename, it's executed in a subshell. source is just a way of telling the computer to run it in the current shell. I'm not sure why these don't require the execute permission though.
./ is pretty straightforward. However, I've seen people run scripts without the ./. One told me I could do this by doing something with PATH. I completely don't understand this PATH thing.
Can someone explain in the easiest way possible?
On
bash script1
you are executing bash (the one that needs execution permission) to read and process script1 (which needs read permission).
On
source script1
you are telling the current bash to read the file and process it as if it were typed on the current shell, so the current bash reads the script (read permission) and executes every line.
Finally, on
./script1
you are telling bash to try to run a file called ./script1, so it checks if it is executable (execute permission on ./script1) and passes this file to the kernel to be executed. The kernel opens the file and acts as needed (if it have a shebang line, it uses whatever is given, if it finds it is an ELF object, it prepares the binary in memory...).
Regarding PATH, check some documentation and come back with specific doubts, if any.
PATH is environmental variable. It is a list containing all directories that will be searched when you issue a command. So if your PATH is defined as PATH=/bin:/usr/bin these two dirs will be searched, if you redefine it as export PATH=./:$PATH it will also add current directory to search list.
I am already using a makefile, and I was hoping to be able to use it to store a few useful aliases that the user could then invoke. I know that I can make a bash file with the aliases already built in, so and run it with source, so I can do something like:
# File: aliases.sh
alias useful="command to run"
alias also-useful="another command -to run"
Then I can run this in the current terminal session with:
source ./aliases.sh
Using a Makefile
So I was hoping to achieve something similar with a makefile, I was hoping to have a simple aliases entry, so the user could just run:
make aliases
I prefer to avoid adding an extra file if this is at all possible, because I don't want to add extra files for such simple tasks. If you have any suggestions that would be better, I'd be open to hearing them too.
If what you're asking is for make aliases to create aliases that you can then invoke at your shell prompt, something like:
$ make aliases
$ useful
then that is impossible and the reason has nothing to do with make.
In a UNIX/POSIX system the process hierarchy is strict: a process starts one or more sub-processes, and each of those can start more, etc. So a login manager process starts your shell (or your window manager), your shell starts make, which is another process, and make will run another shell as a subprocess to run each recipe, and each shell will run programs like compilers, commands like rm which are also processes, etc.
It is a fundamental rule of all processes that they cannot modify the environment (memory) of their parents (and they can only modify the environment of their children before they are started). So, if you start a new shell and change your working directory then exit that shell, the parent's shell is not changed. If you set an environment variable in the child process, the variable is not set in the parent. Etc.
Shell aliases are part of a particular shell's memory. So a program you start cannot create aliases in its parent shell. It doesn't matter if that program is make or anything else.
That's why you have to use the special command source to load those into your shell: instead of running a new shell, the source command tells the current shell to run the commands in the script as if you'd typed them in at the command line... so no new process is created and the current shell's environment and memory is modified. If you ran your aliases file as a shell script, via aliases rather than source aliases, then a new shell would be created, the aliases would be defined, then the shell would exit and all the aliases would be gone again.
So, all that to say it's not possible for make to define aliases in the shell that invokes it: the operating system won't allow it.
I would like to be able to have a rule inside my makefile that a changes the stack size and that appends to LD_LIBRARY_FILE the current directory.
In the shell I can just do:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)
ulimit -s SOME_NUMBER
Is there way to make a rule inside a makefile that is equivalent to running this two commands and will update the environment variables after the makefile is run?
You can link the executables with -z stack-size=VALUE or -rpath, which might have a similar effect.
You could push the command characters to the TTY layer and hope that the shell will execute them, but that is a bit nasty. The makefile could also compile a program which does the job using ptrace, but that would be very ugly as well.
No
A child process in bash cannot effect the environment of the calling shell (as #mpez pointed out). This is done on purpose to avoid some security holes. It's possible to export the commands to change the environment variable to a script, and run that script after you run make (which can be automated through another script), but make itself cannot effect the environment of the calling shell.
I have a shell script on a mac (OSX 10.9) named msii810161816_TMP_CMD with the following content.
matlab
When I execute it, I get
./msii810161816_TMP_CMD: line 1: matlab: command not found
However, when I type matlab into the shell directly it starts as normal. How can it be that the same command works inside the shell but not inside a shell script? I copy-pasted the command directly from the script into the shell and it worked ...
PS: When I replace the content of the script with
echo matlab
I get the desired result, so I can definitely execute the shell script (I use ./msii810161816_TMP_CMD)
Thanks guys!
By default, aliases are not expanded in non-interactive shells, which is what shell scripts are. Aliases are intended to be used by a person at the keyboard as a typing aid.
If your goal is to not have to type the full path to matlab, instead of creating an alias you should modify your $PATH. Add /Applications/MATLAB_R2014a.app/bin to your $PATH environment variable and then both you and your shell scripts will be able to simply say
matlab
This is because, as commenters have stated, the PATH variable inside of the shell executing the script does not include the directory containing the matlab executable.
When a command name is used, like "matlab", your shell looks at every directory in the PATH in order, searching for one containing an executable file with the name "matlab".
Without going into too much detail, the PATH is determined by the shell being invoked.
When you execute bash, it combines a global setting for basic directories that must be in the PATH with any settings in your ~/.bashrc which alter the PATH.
Most likely, you are not running your script in a shell where the PATH includes matlab's directory.
To verify this, you can take the following steps:
Run which matlab. This will show you the path to the matlab executable.
Run echo "$PATH". This will show you your current PATH settings. Note that the directory from which matlab is included in the colon-separated list.
Add a line to the beginning of your script that does echo "$PATH". Note that the directory from which matlab is not included.
To resolve this, ensure that your script is executed in a shell that has the needed directory in the PATH.
You can do this a few ways, but the two most highly recommended ones would be
Add a shebang line to the start of your script. Assuming that you want to run it with bash, do #!/bin/bash or whatever the path to your bash interpreter is.
The shebang line is not actually fully standardized by POSIX, so BSD-derived systems like OSX will happily handle multiple arguments to the shebanged executable, while Linux systems pass at most one argument.
In spite of this, the shebang is an easy and simple way to document what should be used to execute the script, so it's a good solution.
Explicitly invoke your script with a shell as its interpreter, as in bash myscript.sh or tcsh myscript.sh or even sh myscript.sh
This is not incompatible with using a shebang line, and using both is a common practice.
I believe that the default shell on OSX is always bash, so you should start by trying with that.
If these instructions don't help, then you'll have to dig deeper to find out why or how the PATH is being altered between the calling context and the script's internal context.
Ultimately, this is almost certainly the source of your issue.
Let's say a script is called with /bin/sh. Is it possible to source another script from that script and to have it be interpreted with #!/bin/bash?
It would appear that the #!/bin/bash doesn't do anything...
And by source, at this point I am meaning the functionality of manipulating the parent environment.
No. The whole point of sourcing a script is that the script is interpreted by the shell doing the sourcing. If, as is often the case, /bin/sh is bash, then you will get the desired behavior. Otherwise, you are out of luck.
Try the source command, or dot operator. You might also try the env command. Note, make sure you export if you're using source (or dot).