Initializing variables in bash - unix

I'm migrating windows CMD script to bin/bash on unix.
The goal of initial script was to setting up some variables, so after anything run from this cmd window was using that variables.
How can I do same in UNIX? Looks like simple
MyVar="value"
doesn't work. It visible only in script itself, not from terminal where it was run.

You can initialize shell variables with simple assignments
$ foo="fooval"
$ echo $foo
fooval
These variables won't spread to unrelated child processes:
$ foo=fooval
$ sh -c 'printf "\"%s\"" $foo'
""
To make them spread, you need to export them into the process's (shell's)
environment (make them into "environment variables" (these are commonly capitalized, i.e.,
FOO instead of foo)
$ export foo
$ sh -c 'echo $foo'
fooval
You can assign and export in one step:
$ export foo=fooval
Environment variables will never spread anywhere but down the process hierarchy.
(Only to children, never to parents or completely unrelated processes)
Therefore, if you have a script with variable assignments, you need to source it, not execute it:
$ ./envvars #won't affect the parent shell
$ . ./envvars #this will
There are no per-terminal variables (though there are per-terminal configurations with fixed keys accessible manipulatable with the stty tool).

Create a file test.sh
add the following line:
export b="key"
Now goto the terminal and do the following :
source ./test.sh
echo $b
Output:
key

Related

sleep inside inotifywait in a shell function not working

I am trying to run the following function
foo () {
sleep 1
echo "outside inotify"
(inotifywait . -e create |
while read path action file; do
echo "test"
sleep 1
done)
echo "end"
}
Until inotifywait it runs correctly; I see:
>> foo
outside inotify
Setting up watches.
Watches established.
However as soon as I create a file, I get
>>> fooo
outside inotify
Setting up watches.
Watches established.
test
foo:6: command not found: sleep
end
Any idea why? Plus do I need to spawn the subprocess ( ) around inotifywait? what are the benefits?
thank you.
Edit
I realized I am running on zsh
The read path is messing you up, because unlike POSIX-compliant shells -- which guarantee that only modification to variables with all-uppercase names can have unwanted side effects on the shell itself -- zsh also has special-cased behavior for several lower-case names, including path.
In particular, zsh presents path as an array corresponding to the values in PATH. Assigning a string to this array will overwrite your PATH as well.

How to set and use variable in tmux.conf depending on whether an environment variable is set

(Disclaimer: I am fully aware that there are solutions to the problem I describe below that involve writing and calling shell scripts that interact with a running tmux server, or set the necessary environment variables before starting the tmux server. I am specifically posting this questions to see if it possible to solve this problem without the use of such scripts.)
Problem Summary
In my .tmux.conf file, I am trying to set a local variable VALUE to different values depending on whether an environment variable FOO has been set before invoking tmux or not. I then want to use VALUE in other tmux commands. Unfortunately, I either cannot set VALUE correctly or access it after it has been set.
Previous Attempts
According to what I have found in the manpage and in other Q&A posts that contain sample tmux code, there are several ways to implement the above.
Attempt 1
I first tried using the if-shell command. I attempted using this command both with and without the -b flag; the result was the same in either case.
I have seen from examples that I can assign variables with the syntax VALUE=bar. Given that, here is a minimal example of my configuration:
if-shell '[ -z "$FOO" ]' \
'VALUE=bar' \
'VALUE=baz'
set-environment -g RESULT $VALUE
Terminal session:
$ echo $FOO
$ tmux
[detached (from session 0)]
$ tmux showenv -g VALUE
VALUE=bar
$ tmux showenv -g RESULT
RESULT=
$ killall tmux
$ export FOO=foo
$ echo $FOO
foo
$ tmux
[detached (from session 0)]
$ tmux showenv -g VALUE
VALUE=baz
$ tmux showenv -g RESULT
RESULT=
So while VALUE seems to have been set correctly, RESULT does not seem to able to access VALUE.
Attempt 2
The manpage also mentions that commands can be conditionally executed using %if statements. Using this format, I tried the following configuration:
%if #{#(if [ -z "$FOO" ]; then echo 1; else echo 0)}
VALUE=bar
%else
VALUE=baz
%endif
set-environment -g RESULT $VALUE
For the expression in the %if statement, I tried several variations, such as
#{#([ -z "$FOO" ])} (I believe this shouldn't work since this command does not produce any output, but it was worth a try.)
#{==:#(if [-z "$FOO" ]; then echo 1; else echo 0),1} (Just in case an explicit comparison would work)
Even with these variations, regardless of whether FOO was set or not, I got the following:
$ tmux
[detached (from session 0)]
$ tmux showenv -g VALUE
VALUE=baz
$ tmux showenv -g RESULT
RESULT=baz
Thus while VALUE was accessible, it was always baz.
Unfortunately, I have been able to find no useful examples regarding the formats used in conditional statements. The manpage describes how to access tmux variables and some formatting hints; however, regarding accessing environment variables, all I could find was a way to use shell commands:
In addition, the first line of a shell command's output may be inserted using #(). For example, #(uptime) will insert the system's uptime. When constructing formats, tmux does not wait for #() commands to finish; instead, the previous result from running the same command is used, or a placeholder if the command has not been run before.
I am unsure of whether this means I need to call commands in #() twice to avoid using a placeholder value, which may be a possible error on my part.
I was also unable to find a way to print the result of #{} commands easily to debug this part of the statement.
Summary of Questions
While I would appreciate any pointers to information that may help me solve this problem, the most pressing questions for me are:
Why is VALUE being set correctly, yet not accessible to RESULT in Attempt 1?
How should my conditional be written in Attempt 2 to ensure that VALUE is set correctly?
The way tmux runs the config is by parsing the config file into a set of commands, and then executing them (there is a command queue, so the config file is parsed and appended to the queue and then executed from the queue). So there are distinct parse and execution steps.
The problem you are running into with attempt 1, is that the if-shell is run at execution time, but the $VALUE expansion happens at parse time. VALUE is not set when the set-environment command is parsed.
In attempt 2, #() is not processed inside %if so that won't work. However, you can use the variable directly in formats (if it is set). %if happens at parse time.
So you need to make sure assignment and expansion happen in the right order. You have a couple of choices.
You could make tmux expand the variable at command execution time rather than parse time. You can do this by wrapping the setenv inside run-shell, so something like:
if-shell '[ -z "$FOO" ]' \
'VALUE=bar' \
'VALUE=baz'
run 'tmux setenv -g RESULT $VALUE'
Or you could do the assignment at parse time like you tried in attempt 2, but you can't use #() - you need to use a format instead:
%if #{==:#{FOO},}
VALUE=bar
%else
VALUE=baz
%endif
setenv -g RESULT $VALUE
(Note that X=Y in the config file is equivalent to setenv -g X=Y except it happens when parsing rather than executing - both set the global environment. So you could get rid of VALUE and do either RESULT=bar or setenv -g RESULT bar inside the %if.)
Also you can use display -p to print formats. In master and 2.9 you can add -v to see how they are parsed:
$ tmux setenv -g FOO bar
$ tmux display -pv '#{==:#{FOO},baz}'
# expanding format: #{==:#{FOO},baz}
# found #{}: ==:#{FOO},baz
# modifier 0 is ==
# expanding format: #{FOO}
# found #{}: FOO
# format 'FOO' found: bar
# replaced 'FOO' with 'bar'
# result is: bar
# expanding format: baz
# result is: baz
# compare == left is: bar
# compare == right is: baz
# replaced '==:#{FOO},baz' with '0'
# result is: 0
0

Sharing config variables between bash and R

I need to share config variables between various R and bash programmes. They all share various resources, especially a GRASS database.
I started by creating a bash script that sets up shell variables and then runs the R programme. This way R is blind to the shell variables:
$ cat testVars.R
Sys.getenv(c("WDIR","GDIR"))
$ cat testVars.sh
#!/bin/sh
WDIR="/Work/Project/"
GDIR=$WDIR"GRASSDATA"
Rscript testVars.R
$ ./testVars.sh
WDIR GDIR
"" ""
I then tried using the readRenviron function in R, thinking it could be used to source a bash file setting up variables. However, this results in a different problem, R is unable to replace and concatenate variables like bash does:
$ cat testVars.R
readRenviron("./testVars.sh")
Sys.getenv(c("WDIR","GDIR"))
$ cat testVars.sh
#!/bin/sh
WDIR="/Work/Project/"
GDIR=$WDIR"GRASSDATA"
$ Rscript testVars.R
WDIR GDIR
"/Work/Project/" "$WDIRGRASSDATA"
YAML is supported to some extent in both languages, but it suffers from the same lack of replacement and concatenation facilities. For instance, with YAML I would need to repeat the working directory countless times in the configuration file.
So here is what I am looking for: a configuration format than can be used by both R and bash and also allows variable concatenation.
I think all you need is to export variables in bash to make them accessible to R.
$ export TEST_VAR=42
$ Rscript -e "Sys.getenv('TEST_VAR')"
[1] "42"
Then concatenation can be handled with paste() or paste0().

Setting environment variable in shell script does not make it visible to the shell

I want to use a shell script that I can call to set some environment variables. However, after the execution of the script, I don't see the environment variable using "printenv" in bash.
Here is my script:
#!/bin/bash
echo "Hello!"
export MYVAR=boubou
echo "After setting MYVAR!"
When I do "./test.sh", I see:
Hello!
After setting MYVAR!
When I do "printenv MYVAR", I see nothing.
Can you tell me what I'm doing wrong?
This is how environment variables work. Every process has a copy of the environment. Any changes that the process makes to its copy propagate to the process's children. They do not, however, propagate to the process's parent.
One way to get around this is by using the source command:
source ./test.sh
or
. ./test.sh
(the two forms are synonymous).
When you do this, instead of running the script in a sub-shell, bash will execute each command in the script as if it were typed at the prompt.
Another alternative would be to have the script print the variables you want to set, with echo export VAR=value and do eval "$(./test.sh)" in your main shell. This is the approach used by various programs [e.g. resize, dircolors] that provide environment variables to set.
This only works if the script has no other output (or if any other output appears on stderr, with >&2)

In UNIX shell scripting: What does $! expand to?

What is the meaning for $! in shell or shell scripting? I am trying to understand a script which has the something like the following.
local#usr> a=1
local#usr> echo $a
1
local#usr> echo $!a
a
It is printing the variable back. Is it all for that? What are the other $x options we have? Few I know are $$, $*, $?. If anyone can point me to a good source, it will be helpful. BTW, This is in Sun OS 5.8, KSH.
The various $… variables are described in Bash manual. According to the manual $! expands to the PID of the last process launched in background. See:
$ echo "Foo"
Foo
$ echo $!
$ true&
[1] 67064
$ echo $!
67064
[1]+ Done true
In ksh it seems to do the same.
From the ksh man page on my system:
${!vname}
Expands to the name of the variable referred to by vname. This
will be vname except when vname is a name reference.
For the shell you are asking, ksh, use the the ksh manual, and read this:
Parameter Substitution
A parameter is an identifier, one or more digits, or any of
the characters *, #, #, ?, -, $, and !.
It is clear that those are the accepted options $*, $#, $#, $?, $-, $$, and $!.
More could be included in the future.
For the parameter $!, from the manual:
"!" The process number of the last background command invoked.
if you start a background process, like sleep 60 &, then there will be a process number for such process, and the parameter $! will print its number.
$ sleep 60 &
[1] 12329
$ echo "$!"
12329
If there is no background process in execution (as when the shell starts), the exansion is empty. It has a null value.
$ ksh -c 'echo $!'
If there is a background process, it will expand to the PID of such process:
$ ksh -c 'sleep 30 & echo $!'
42586
That is why echo $!a expanded to a. It is because there is no PID to report:
$ ksh -c 'echo $!a'
a
Other shells may have a different (usually pretty similar) list of expansions (a parameter with only one $ and one next character).
For example, bash recognize this *##?-$!0_ as "Special parameters". Search the Bash manual for the heading "3.4.2 Special Parameters".
Special Parameters
The shell treats several parameters specially.
It gives the Process id of last backgroundjob or background function
Please go through this link below
http://www.well.ox.ac.uk/~johnb/comp/unix/ksh.html#specvar
! is a reference operator in unix, though it is not called with that name.
It always refers to a mother process. Try typing :! in vi, it takes you to command prompt and you can execute commands as usual until exit command.
! in SQLPLUS also executes the command from the command prompt. try this in sqlplus
SQL> !ls --- this gives the list of files inthe current dir.
$! - obviously gives the process id of the current/latest process.

Resources