In a makefile I'm reading, there are many references to a variable, projdir, defined and exported in the shell script that calls make; there are many instances of $(projdir) in the makefile.
From the make manual at http://www.gnu.org/software/make/manual/make.html#Reference, there are only two types of variables: recursively expanded variables and simply expanded variables. It seems projdir is neither one.
Question 1: Since the makefile works, it must be true that makefiles can access shell variables defined in the parent shell environment. Why is this not documented (or perhaps I did not find the correct documentation)?
Question 2: Unrelated to question 1, I see in Section 6.2 the line all:;echo $(foo). Why is the semicolon ; needed here?
Question 1. Environment variables automatically become make variables. This is explained in section 6.10:
Variables in make can come from the environment in which make is run. Every environment variable that make sees when it starts up is transformed into a make variable with the same name and value.
Question 2: Putting a semicolon after a rule name and optional prerequisites allows you to start the recipe on the same line. This is explained in section 4.2:
The first recipe line may appear on the line after the prerequisites, with a tab character, or may appear on the same line, with a semicolon.
They used this syntax in the example in 6.2 for brevity, so they could show the whole rule inline in the sentence; I think it's rarely used in practice.
From the make manual:
Variables in make can come from the environment in which make is run. Every environment variable that make sees when it starts up is transformed into a make variable with the same name and value.
As to your second question:
Why is the semicolon ; needed here?
Without the semicolon the right-hand side of : would specify the list of dependencies. The semicolon terminates that (empty) list.
Variables have both a flavor and an origin.
Expansion type is a flavor.
make contains a flavor function which can be used to find the type of variable in question. It will return one of undefined, recursive or simple.
Variables also have an origin which is where their value has come from. What you are seeing here is one of those possible origins.
The origin function will tell you the origin of a variable.
There are seven possible origins: undefined, default, environment, environment override, file, command line, override and automatic.
So your projdir variable has a flavor of simple and an origin of environment.
Related
The help text for set -a is somewhat cryptic to me, but maybe i just don't understand what bash is doing behind the scenes with variables. Can someone explain why "marking" variables is needed (what are "unmarked" variables)?
My bash man-page uses a slightly different wording for this: Each variable or function that is created or modified is given the export attribute and marked for export to the environment of subsequent commands. This sounds more clear to me. It means:
Each variable you define in your script, is placed in the environment of any child process you create, and each function you define is also available to each bash-child-process you create.
I am writing a shell.
With the execvpe system call, I can run a program and control its environment. What are the minimum values I need to pass through here?
Alternatively, I understand that child processes should have a copy of their parent's environment, possibly with some values added. While testing my shell, I am running it from within bash from within my terminal from within a window manager, etc etc. What are the bare basics that I can assume are in my environment? If I were to run my shell straight from a TTY (the "lowest level", as far as I understand), what can I expect?
That’s a very broad question. To a certain extent,
programs should be able to run with no environment at all.
“X” display (i.e., GUI) programs need to know
where they are supposed to display.
This information is usually provided
through the DISPLAY environment variable,
but can also be passed on the command line.
There are probably other environment variables that are essential
(or nearly so) to “X” programs;
it’s been a while since I’ve looked under that hood.
Any program that needs to use special characteristics of your terminal
needs the TERM environment variable.
“Special characteristics” means being able to set colors
(as ls and grep can do, subject to options),
move around the screen (like vi / vim),
or even know the size of the screen (like less).
Note that size of the screen may also be available
through ROWS and COLUMNS.
Any program that needs to know the date and time
as perceived / understood by the user needs to know the time zone (TZ) —
although, if you’re willing to work with absolute (GMT / UTC),
you don’t need this.
etc.
The minimum that you need is a working PATH variable. Any extras beyond that depend on what programs you want to execute.
POSIX has a list of commonly-used environment variables, very few programs use more than a few of those.
Generally if you're using execvp*, you're not giving full pathnames for the executables. It makes your programs much simpler, you do not have to provide a full pathname for each executable, as is needed by the plain execv. POSIX describes these functions as
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
and (referring to the parameters of the various exec* functions):
The argument path points to a pathname that identifies the new process image file.
The argument file is used to construct a pathname that identifies the new process image file. If the file argument contains a slash character, the file argument shall be used as the pathname for this file. Otherwise, the path prefix for this file is obtained by a search of the directories passed as the environment variable PATH (see XBD Environment Variables). If this environment variable is not present, the results of the search are implementation-defined.
and (remember that "file" is referring to execvp rather than execv, so the environ variable applies to the search using PATH for the "file" parameter):
For those forms not containing an envp pointer (execl(), execv(), execlp(), and execvp()), the environment for the new process image shall be taken from the external variable environ in the calling process.
So... you could technically remove the entire PATH variable, but the result would be implementation-defined.
The minimum neccessary environment is empty. You don't need anything.
e.g.
$ env -i env
$
We can see that env -i has created a blank environment.
We can take this further:
$ env -i /bin/bash
sweh#server:/home/sweh$ env
LS_COLORS=
PWD=/home/sweh
SHLVL=1
_=/usr/bin/env
We can see that bash has set a few variables, but nothing was inherited.
Now such an environment may break some things; e.g. a missing TERM variable means that vi or less may not work properly
$ less foo
WARNING: terminal is not fully functional
foo (press RETURN)
So, really, you need to determine what programs you expect to run inside the environment and what their needs are.
What is the best approach for overriding variable when launching make command?
make PREFIX="/new_path"
PREFIX="/new_path" make
I suspect that it's not exactly the same since in the first case the overriding is specified as a parameter of the make command, while in the second case (if I guess well) we use the shell definition for setting the variable then call the command make.
As you say, it depends on what you mean by "best". Both are appropriate in different circumstances.
By default makefile variable assignments take precedence over environment variable settings, and command line arguments override makefile variable assignments.
So, if you want to be sure the setting you give is used, then you should always use the command line: make PREFIX="new/path". If you want to provide a default value for a makefile to use if it doesn't already have a value, you should use the environment.
I am using bash.
There is an environment variable that I want to either append if it is already set like:
PATH=$PATH":/path/to/bin"
Or if it doesn't already exist I want to simply set it:
PATH="/path/to/bin"
Is there a one line statement to do this?
Obviously the PATH environment variable is pretty much always set but it was easiest to write this question with.
A little improvement on Michael Burr's answer. This works with set -u (set -o nounset) as well:
PATH=${PATH:+$PATH:}/path/to/bin
PATH=${PATH}${PATH:+:}/path/to/bin
${PATH} evaluates to nothing if PATH is not set/empty, otherwise it evaluates to the current path
${PATH:+:} evaluates to nothing if PATH is not set, otherwise it evaluates to ":"
The answers from Michael Burr and user spbnick are already excellent and illustrate the principle. I just want to add two more details:
In their versions, the new path is added to the end of PATH. This is what the OP asked, but it is a less common practice. Adding to the end means that the commands will only be picked if no other commands match from earlier paths. More commonly, users will add to the front to path. This is not what the OP asked, but for other users coming here it may be closer to what they expect. Since the syntax is different I'm highlighting it here.
Also, in the previous versions, the PATH is not quoted. While its unlikely on most Un*x-like operating systems to have spaces in PATH, it is still better practice to always quote.
My slightly improved version, for most typical use cases, is
PATH="/path/to/bin${PATH:+:$PATH}"
What does VAR_NAME=${VAR_NAME:-"/some/path/file"} mean in an shell script?
This is for an init script, I'm writing a custom one to get some of our startup operations into init scripts so that we can start them automatically on boot, but I don't have much experience with shell scripting so I'm using a startup script for an unrelated piece of software that's we've customized in the past.
The path pointed to is to a file that contains configuration values that override defaults set in the script.
I'm having trouble figuring out what that construct really means (the :- part in particular).
The script I'm working off of also seems to chain this operation together to resolve which value to use such as:
LOG_FILE=${LOG_FILE:-${LOGFILE:-$DEFAULT_LOG_FILE}}
${parameter:-word}
Use Default Values. If parameter is unset or null, the expansion
of word shall be substituted; otherwise, the value of parameter shall be
substituted.
It sets VAR_NAME equal to VAR_NAME if it exists or /some/path/file if it doesn't.
Chaining it would only make sense if the variable names were different going down the chain.