How to check all variables are being used in a bash script? - bash

Is there are way to check that all variables declared in a bash script are being used? Something analogous to -Wall in c++?
Cheers

There's no built-in way to do this, no, and it can never be done perfectly, because of situations like these:
Suppose the user's Bash profile exports CLASSPATH as an environment variable. Then a Bash script might include a statement like CLASSPATH=... but never refer to CLASSPATH, if its only purpose in assigning to CLASSPATH is to modify the behavior of some program that uses the environment variable (such as java).
Suppose that I create variables a and b, and a variable c whose value is obtained from the user and can be either a or b. I can then use ${!c} to obtain the value of the user-specified variable; a given run of the script might never refer to b (because c is set to 'a'), but a different run of the script might do differently.
That said, you might be interested in the -u option to the set builtin. If your script contains this command:
set -u
then from that point on, it will be an error to refer to a variable or parameter that has not been set. This can help detect typos in variable-names and whatnot. This is obviously much less than what gcc -Wall does (since gcc always gives an error-message when you refer to an undeclared variable), but you may find it beneficial in the same way.

Related

Restore previous value or unset environment variable

I have something like that in a shell script
cd /some/folder1
savedCFLAGS=${CFLAGS-}
export CFLAGS="-fexceptions ${CFLAGS-}"
./configure --some-option -what-ever
make
export CFLAGS=${savedCFLAGS}
cd /some/folder2
./configure --some-option -what-ever
make
This works more or less correctly but has one serious flaw.If CFLAGS was unset previously, after the first block it will be set and empty.Sometimes, this is a problem, because some configure scripts use default values for variables only if they are actually unset - default value will not be used if the variable is simply empty.
Let me also state that I have such constructs multiple times in the script, with multiple different variables(this is a script used to build complete GCC-based toolchain).
What would be the easiest way to have the variable either restored to previous value (if it actually was set previously) or unset (if it was unset before my modifications)? I could set savedCFLAGS to some special token if it was unset, and then test whether savedCFLAGS has a value equal to that token.However, maybe there's something much simpler?
Just run your setenv+configure+make in a subshell using parentheses:
(export CFLAGS="-fexceptions ${CFLAGS-}";./configure --some-option -what-ever; make)
It inherits the environment in a copy, so when exiting the sub-shell you get the exact same "previous" state: no env. variables are ever changed in your script, and further configure commands work properly.

How to set environment variables in tcl?

When I source my .cshrc file and run the Tcl script it is working fine:
$ source .cshrc-sample
$ tclsh invoke.tcl
Following is the .cshrc file:
setenv AUTOTEST "/auto/isbutest/frt"
setenv ATS_EASY "$AUTOTEST"
setenv ATS_USER_PATH "$AUTOTEST"
setenv PATH "${AUTOTEST}/bin:${PATH}"
But when I tried setting the environment variable in Tcl itself and run the script,
I get the following error:
$ tclsh invoke.tcl
can't find package ha
while executing
"package require ha"
(file "invoke.tcl" line 8)
My Tcl script - invoke.tcl:
global env
set env(AUTOTEST) "/auto/isbutest/frt"
set env(ATS_EASY) "/auto/isbutest/frt"
set env(ATS_USER_PATH) "/auto/isbutest/frt"
set env(PATH) "$env(PATH):/auto/isbutest/frt/bin:";
package require ha
How can I run the script without sourcing the .cshrc?
The thing is setting environment variable is not possible using scripts, the lifetime of the variable is within the runtime of the script. When I tried printing the PATH variable it shows what is needed, but I don't know why it is not working. Is there any other workaround for this?
There's a few possibilities. The key things to look at are whether there are any other environment variables that you've missed out, whether the Tcl auto_path global variable is correct immediately before the package require, and whether there is anything else going on.
The easiest way from the Tcl side is to add:
puts "auto_path=$auto_path"
parray env
immediately before the package require that has the error. That should print out plenty of information. (Pay particular attention to if you are setting the TCL_LIBRARY or TCLLIBPATH environment variables differently.)
Aside from that, it's possible that there is something set in the ~/.tclshrc file, which is only sourced in interactive mode (it happens before you get your prompt). That could cause observable changes. Another option is if the ha package's pkgIndex.tcl script is written to use abbreviated commands, which only work when Tcl is in interactive mode. Errors in the package index definition script will make the code that describes how to actually load/source the package's implementation not register, and could give you the error state you see. If the script is assuming it can use abbreviations, fix it as that's always a bug. Abbreviations are a convenience when using Tcl interactively, and should never be put in proper saved code.
You might want to check whether the list of packages is complete. Use this code for that:
catch {package require NoSuchPackage}; # Force immediate population of the list of packages
puts Packages:\n\t[join [lsort -dictionary [package names]] \n\t]
Again, put this in after any setting of global variables and before the problem package require.
In side tcl script, you can simply do setenv as, setenv AUTOTEST="/auto/isbutest/frt".
if you want to set a variable, use set VARNAME "/auto/isbutest/frt".
if you want to get any environment variable, use $::env(AUTOTEST).
and any variable declared using set command can be accessed using $VARNAME.

Shell (tcsh) command to change the value corresponding to a key

What should be the Shell (tcsh) command if i want to change the value corresponding to the key in a properties file. For Example: Key=Value to Key=SomeOtherValue
In tcsh (or csh, for that matter), you use the setenv command to write a value to an environment variable.
setenv Key SomeOtherValue
For more information take a look at any csh reference (because it's more widely used, and tcsh is completely compatible.)
For example, see here.
Update: OP has now clarified the the wants to edit the content of a properties file, not an environment variable.
Here is a good SO question/answer that solves this different problem nicely. It uses bash, but translating should be easy.
Changing contents of a file through shell script

What's the shell script meaning?

I am a newer in shell script, following is a shell script:
$JAVA_HOME/bin/java -Dpid=MyJava \
-Xms${HEAP_MIN}m -Xmx${HEAP_MAX}m -cp ${CPG_CLASSPATH} \
-Dconfig=${CFG_FILE} \
-Dcom.test.eps.configpath=${my_config}/ \
-Dcom.test.eps.rt.config=${my_config}/ \
-Dlog4j.configuration=file:///${my_config}/log4j.properties.ewf.rt \
com.test.MyJava &
Can anyone tell me the code meaning of every line above?
$JAVA_HOME/bin/java — invokes the JRE binary located at the bin path of the folder designated by the $JAVA_HOME variable set in the current user's enviornment. That is, it runs Java, specifically the version pointed at by JAVA_HOME.
The trailing \s are escape charecters which escape the newline at the end of the line. Normally in a shell program a newline at the end of the line tells the shell that you're done with the command and it can interpret it now. Ending a line with \ tells the shell that the command will actually continue on the next line, i.e. this is all one command.
-Dpid=myJava — sets a system property for the jre named pid with value myJava. Java programs can basically ask, getProperty("pid") at runtime and it will return "myJava" and then choose its behavior appropriately, this is a way of configuring the programs the the JRE runs.
-Xms${HEAP_MIN}m — sets javas min heap size to value in ${HEAP_MIN} env var. The heap size is how much memory the jre sets aside to store its stack trace.
-Xmx${HEAP_MAX}m — sets Java's max heap size to value in ${HEAP_MAX} env var.
-cp ${CPG_CLASSPATH} — sets the Java classpath to the value in the ${CPG_CLASSPATH} env var.
-Dconfig=${CFG_FILE} — sets a system property for the JRE named config with value ${CFG_FILE}.
-Dcom.test.eps.configpath=${my_config}/ — sets a system property for the JRE named com.test.eps.configpath with value ${my_config}.
-Dcom.test.eps.rt.config=${my_config}/ — sets a system property for the JRE named com.test.eps.rt.config with value ${my_config}.
-Dlog4j.configuration=file:///${my_config}/log4j.properties.ewf.rt — sets a system property for the JRE named log4j.configuration with value file:///${my_config}/log4j.properties.ewf.rt.
com.test.MyJava is a Java class essentially located on the classpath at com/test/MyJava.class which presumably has a main function. After the JRE initializes with all of the previous configurations set, it will run this class and run its main function.
& tells the OS to run this command in its own process and not to wait for it to return before the cli gives control back to the user. It's basically telling the OS to run this program in a process separate from the one which is running your shell.
Assuming your on a unix based system, check this out:
http://www.manpagez.com/man/1/java/
That is complicated, so maybe you should learn the basics of shell scripting first:
http://www.freeos.com/guides/lsst/
As shell goes, there is nothing intrinsically complex there; there is, however, an awful lot of the same sort of stuff over and over.
At the highest level, it is a single command. The backslashes at the ends of the lines continue the command with the information on the next line. It invokes the JVM (Java Virtual Machine, which is the key component of the JRE, Java Runtime Environment) found in the directory $JAVA_HOME/bin, where $JAVA_HOME is (I trust you recognize), a shell variable; indeed, it should be an environment variable. The JVM is invoked with 10 arguments (unless I've miscounted again), and is run in the background (that's the & at the end). That means the shell script launches the JVM and does not wait for it to complete.
Some of the arguments also contain the expanded form of shell variables, some of which may be environment variables (you cannot tell definitively which variables are environment variables simply by looking at them, but conventionally, environment variables are all upper-case, like $JAVA_HOME). The alternative notation ${HEAP_MAX} expands a variable HEAP_MAX, just as $HEAP_MAX does. However, in that argument, you cannot (sensibly) write:
-Xmx$HEAP_MAXm # Wrong (in this context)
because that looks for a variable $HEAP_MAXm; you use the braces to delimit the name of the variable. There are also a lot of other things you can do with the name in braces, such as ${JAVA_HOME:?} which says that if $JAVA_HOME is not set, generate an error message and stop the script. See the manual for 'Parameter Expansion'.
For the meanings of the arguments to the JVM, you'll need to look at the man page. As an exercise in shell, though, the other arguments are all variations on what has been discussed already, using different variables to identify the locations of files needed by the JVM or the program it is running.

Save Global variables BASH

I am new at bash and trying to solve some issues for a code I'm trying to make.
I am at the terminal under my user name and connect to bash
USER$
USER$ bash
bash$
now in the bash I am saving some variables f.e:
i=2
k=2
let p=$k*$i
now I want to use those variables outside the bash function
bash$exit
USER$
but now the variables are not there
I try using export, but it did not really work, could use ur help, tnx
Not possible. You cannot set environment variables in a parent process like this.
Unlike a DOS batch file, a Unix shell script cannot directly affect the environment of its calling shell.
You could consider using the . (dot) or source command to read and execute the script in the context of the calling shell. This means that changes made in the script do affect the environment (in general; you can still run into issues with sub-shells).
The other alternative is to have the script that sets the variables write the values in name=value format into a file which the calling script then reads (with . or source again).
The conventional solution is to add the settings to your .profile or . bashrc -- which you should use depends on your specific needs and your local Bash configuration; my first recommendation would be .profile, but then you have to avoid any bashisms because this file is shared with sh (so, no let, for example).
For more specific needs, put the commands in a file, and source it when you need it. You might also want to create a simple script to update the file with your current values.
# source this file to update $HOME/stuff
cat<<HERE>$HOME/stuff
i='$i'
k='$k'
p='$p'
export i k p
HERE
The syntax here is quite simple, but assumes you don't have values which can contain single quotes or otherwise free-form content. How to safely store arbitrary values which you don't have complete control over is a much more complex discussion; I am providing a simple solution for the basic use case where you merely need to save a few simple scalar values, like numbers.
To keep your variables when you connect to a remote system, look at the documentation for the tool you are using to connect. For example, ssh has configuration options for importing environment variables from the local system when starting a remote session.

Resources