What does "export" do in shell programming? [duplicate] - bash

This question already has answers here:
Defining a variable with or without export
(15 answers)
Closed 3 years ago.
As far as I can tell, variable assignment is the same whether it is or is not preceded by "export". What's it for?

Exported variables such as $HOME and $PATH are available to (inherited by) other programs run by the shell that exports them (and the programs run by those other programs, and so on) as environment variables. Regular (non-exported) variables are not available to other programs.
$ env | grep '^variable='
$ # No environment variable called variable
$ variable=Hello # Create local (non-exported) variable with value
$ env | grep '^variable='
$ # Still no environment variable called variable
$ export variable # Mark variable for export to child processes
$ env | grep '^variable='
variable=Hello
$
$ export other_variable=Goodbye # create and initialize exported variable
$ env | grep '^other_variable='
other_variable=Goodbye
$
For more information, see the entry for the export builtin in the GNU Bash manual, and also the sections on command execution environment and environment.
Note that non-exported variables will be available to subshells run via ( ... ) and similar notations because those subshells are direct clones of the main shell:
$ othervar=present
$ (echo $othervar; echo $variable; variable=elephant; echo $variable)
present
Hello
elephant
$ echo $variable
Hello
$
The subshell can change its own copy of any variable, exported or not, and may affect the values seen by the processes it runs, but the subshell's changes cannot affect the variable in the parent shell, of course.
Some information about subshells can be found under command grouping and command execution environment in the Bash manual.

it makes the assignment visible to subprocesses.
$ foo=bar
$ bash -c 'echo $foo'
$ export foo
$ bash -c 'echo $foo'
bar

Well, it generally depends on the shell. For bash, it marks the variable as "exportable" meaning that it will show up in the environment for any child processes you run.
Non-exported variables are only visible from the current process (the shell).
From the bash man page:
export [-fn] [name[=word]] ...
export -p
The supplied names are marked for automatic export to the environment of subsequently executed commands.
If the -f option is given, the names refer to functions. If no names are given, or if the -p option is supplied, a list of all names that are exported in this shell is printed.
The -n option causes the export property to be removed from each name.
If a variable name is followed by =word, the value of the variable is set to word.
export returns an exit status of 0 unless an invalid option is encountered, one of the names is not a valid shell variable name, or -f is supplied with a name that is not a function.
You can also set variables as exportable with the typeset command and automatically mark all future variable creations or modifications as such, with set -a.

Related

Assigning a variable in a shell script for use outside of the script

I have a shell script that sets a variable. I can access it inside the script, but I can't outside of it. Is it possible to make the variable global?
Accessing the variable before it's created returns nothing, as expected:
$ echo $mac
$
Creating the script to create the variable:
#!/bin/bash
mac=$(cat \/sys\/class\/net\/eth0\/address)
echo $mac
exit 0
Running the script gives the current mac address, as expected:
$ ./mac.sh
12:34:56:ab:cd:ef
$
Accessing the variable after its created returns nothing, NOT expected:
$ echo $mac
$
Is there a way I can access this variable at the command line and in other scripts?
A child process can't affect the parent process like that.
You have to use the . (dot) command — or, if you like C shell notations, the source command — to read the script (hence . script or source script):
. ./mac.sh
source ./mac.sh
Or you generate the assignment on standard output and use eval $(script) to set the variable:
$ cat mac.sh
#!/bin/bash
echo mac=$(cat /sys/class/net/eth0/address)
$ bash mac.sh
mac=12:34:56:ab:cd:ef
$ eval $(bash mac.sh)
$ echo $mac
12:34:56:ab:cd:ef
$
Note that if you use no slashes in specifying the script for the dot or source command, then the shell searches for the script in the directories listed in $PATH. The script does not have to be executable; readable is sufficient (and being read-only is beneficial in that you can't run the script accidentally).
It's not clear what all the backslashes in the pathname were supposed to do other than confuse; they're unnecessary.
See ssh-agent for precedent in generating a script like that.

Why doesn't LIMIT=\`ulimit -u\` work in bash?

In my program I need to know the maximum number of process I can run. So I write a script. It works when I run it in shell but but when in program using system("./limit.sh"). I work in bash.
Here is my code:
#/bin/bash
LIMIT=\`ulimit -u\`
ACTIVE=\`ps -u | wc -l \`
echo $LIMIT > limit.txt
echo $ACTIVE >> limit.txt
Anyone can help?
Why The Original Fails
Command substitution syntax doesn't work if escaped. When you run:
LIMIT=\`ulimit -u\`
...what you're doing is running a command named
-u`
...with the environment variable named LIMIT containing the value
`ulimit
...and unless you actually have a command that starts with -u and contains a backtick in its name, this can be expected to fail.
This is because using backticks makes characters which would otherwise be syntax into literals, and running a command with one or more var=value pairs preceding it treats those pairs as variables to export in the environment for the duration of that single command.
Doing It Better
#!/bin/bash
limit=$(ulimit -u)
active=$(ps -u | wc -l)
printf '%s\n' "$limit" "$active" >limit.txt
Leave off the backticks.
Use modern $() command substitution syntax.
Avoid multiple redirections.
Avoid all-caps names for your own variables (these names are used for variables with meaning to the OS or system; lowercase names are reserved for application use).
Doing It Right
#!/bin/bash
exec >limit.txt # open limit.txt as output for the rest of the script
ulimit -u # run ulimit -u, inheriting that FD for output
ps -u | wc -l # run your pipeline, likewise with output to the existing FD
You have a typo on the very first line: #/bin/bash should be #!/bin/bash - this is often known as a "shebang" line, for "hash" (#) + "bang" (!)
Without that syntax written correctly, the script is run through the system's default shell, which will see that line as just a comment.
As pointed out in comments, that also means only the standardised options available to the builtin ulimit command, which doesn't include -u.

same variable in different scripts in bash

What better way to use same variable in two different bash scripts?
Simple example:
./set.sh 333
./get.sh
> 333
./set.sh 111
./get.sh
> 111
And how initialize that variable first time?
UPD:
$ cat get.sh
echo "$var"
$ cat set.sh
export var="$1"
$ chmod +x set.sh get.sh
$ source set.sh
$ ./set.sh u
$./get.sh
$ source ./set.sh 2
$ ./get.sh
2
You can have your scripts as:
cat set.sh
export var="$1"
cat get.sh
echo "$var"
chmod +x set.sh get.sh
Then call them:
. ./set.sh 333
./get.sh
333
Please note that . ./set.sh OR source ./set.sh is called sourcing in the script which makes sure that set.sh is executed without creating a sub-shell and variables set in that script are accessible in the other scripts.
What you need to understand is the lifetime of a shell variable (or an environment variable as you are using).
When you run a sub-shell, you are running a child process of the shell, and any shell variables that you set exist for the lifetime of the script. Any environment variables (shell variables are "promoted" to environment variable by the use of export) are copied into the environment of the child process - so changes to environment variables in a child process have NO effect on the value in the parent process.
So what you need to use is source which executes the contents of the script in the current shell (no sub-shell is spawned). Always source set.sh and you should be OK
You have to store that number in a file.
A called shell script is not able to change the variables of the calling shell.
Another way is to source the shell script instead of running it as a separate process.
But maybe you should explain why you think, that you need that feature. Maybe some totally different solution is even better.

what is the significance of exporting path in shell script?

I have seen below two lines in a shell script.
Im new to unix scripting, what is the use of setting this?
PATH=$PATH:/bin:/usr/bin:/usr/sbin:/sbin:/etc:/usr/ucb:/usr/ccs/bin:/usr/local/bin
export PATH
Thanks in advance
If you export something (in bash anyway which I assume is your shell), it will mark that something to be available in subsequently executed commands.
$ FOO=1 # Set the variable
$ echo $FOO # Check the value
1
$ bash # New shell here.
$ echo $FOO # No value since it's not exported
$ exit # Quit the subshell
$ export FOO # Export it
$ bash
$ echo $FOO # It has a value now
1
export is a shell builtin for bash so doing a help export will give you more information on it.
Explicitly exporting the PATH doesn't hurt but generally has no effect as the PATH variable is almost certainly already marked as exported when you launch a shell script.

Unknown error sourcing a script containing 'typeset -r' wrapped in command substitution

I wish to source a script, print the value of a variable this script defines, and then have this value be assigned to a variable on the command line with command substitution wrapping the source/print commands. This works on ksh88 but not on ksh93 and I am wondering why.
$ cat typeset_err.ksh
#!/bin/ksh
unset _typeset_var
typeset -i -r _typeset_var=1
DIR=init # this is the variable I want to print
When run on ksh88 (in this case, an AIX 6.1 box), the output is as follows:
$ A=$(. ./typeset_err.ksh; print $DIR)
$ echo $A
init
When run on ksh93 (in this case, a Linux machine), the output is as follows:
$ A=$(. ./typeset_err.ksh; print $DIR)
-ksh: _typeset_var: is read only
$ print $A
($A is undefined)
The above is just an example script. The actual thing I wish to accomplish is to source a script that sets values to many variables, so that I can print just one of its values, e.g. $DIR, and have $A equal that value. I do not know in advance the value of $DIR, but I need to copy files to $DIR during execution of a different batch script. Therefore the idea I had was to source the script in order to define its variables, print the one I wanted, then have that print's output be assigned to another variable via $(...) syntax. Admittedly a bit of a hack, but I don't want to source the entire sub-script in the batch script's environment because I only need one of its variables.
The typeset -r code in the beginning is the error. The script I'm sourcing contains this in order to provide a semaphore of sorts--to prevent the script from being sourced more than once in the environment. (There is an if statement in the real script that checks for _typeset_var = 1, and exits if it is already set.) So I know I can take this out and get $DIR to print fine, but the constraints of the problem include keeping the typeset -i -r.
In the example script I put an unset in first, to ensure _typeset_var isn't already defined. By the way I do know that it is not possible to unset a typeset -r variable, according to ksh93's man page for ksh.
There are ways to code around this error. The favorite now is to not use typeset, but just set the semaphore without typeset (e.g. _typeset_var=1), but the error with the code as-is remains as a curiosity to me, and I want to see if anyone can explain why this is happening.
By the way, another idea I abandoned was to grep the variable I need out of its containing script, then print that one variable for $A to be set to; however, the variable ($DIR in the example above) might be set to another variable's value (e.g. DIR=$dom/init), and that other variable might be defined earlier in the script; therefore, I need to source the entire script to make sure I all variables are defined so that $DIR is correctly defined when sourcing.
It works fine for me in ksh93 (Version JM 93t+ 2009-05-01). If I do this, though:
$ . ./typeset_err.ksh
$ A=$(. ./typeset_err.ksh; print $DIR)
-ksh: _typeset_var: is read only
So it may be that you're getting that variable typeset -r in the current environment somehow.
Try this
A=$(ksh -c "./typeset_err.ksh && print \$DIR")
or
A=$(env -i ksh -c "./typeset_err.ksh && print \$DIR")

Resources