I have a variable that may or may not be empty. If it's not empty, I want that value. If it is empty, I want the value of some other command. Example:
my_function ()
{
name_override="$1"
user_name=${name_override} || $(git config "user.name")
}
I'm not sure if the code above will work. But basically I want to run git config and store that result in user_name if the name_override variable is unset (and thus $1 would not have been provided).
How can I do this correctly?
The bash idiom for accomplishing this is
user_name=${1:-$(git config "user.name")}
where :- says to use the value of $1 if it is set and non-null, else use the following string.
Related
A trivial situation - the script has finished it's execution, and all the variables used on it's way remained.
I'm looking for a way the script could unset all used by it variables ONLY, as there are many other scripts setting their stuff...
'exec bash' is not an option.
EG from my imagination:
function setVariables {
A="~/"
B=$(du -sh /smth)
C="tralala"
}
setVariables
function cleanup {
readarray -t args < <(setVariables)
set -u "${args[#]}"
}
cleanup
How to achieve this?
Like already suggested in comments, the trivial solution is to put the commands in a script instead of in a function. The script runs in a subprocess with its own environment, and doesn't change anything in the parent. When the child finishes, nothing from its environment is left.
If you need to use a function, Bash has a local keyword which makes the variable's scope local to the current function. When the function ends, all variables are reset to their state from before the function ran (unset if they were unset, previous value if they were set).
If you want to set variables and have them set outside of a function, but have a simple way to revert them to their original value, you really have to build that yourself. Look at how Python's virtualenv does it, for example (a common and popular example, though not necessarily the absolutely most robust) -- it basically writes out the previous values to the definition of the deactivate command which disables the virtual environment and (attempts to) return things to the way they were before you ran activate.
# bash 4+ only!
setVariables () {
declare -A _setvariables_old=([A]="$A" [B]="$B" [C]="$C")
A="~/"
B=$(du -sh /smth)
C="tralala"
}
setVariables
:
cleanup () {
local var
for var in "${!_setvariables_old[#]}"; do
printf -v "$var" "%s" "${_setvariables_old[$var]}"
done
unset _setvariables_old
}
This leaks slightly (what if _setvariables_old is a variable you want to preserve!?) and doesn't know how to unset things; it just reverts to an empty string if something was unset before you started.
I'm writing a bash script, and it needs to check if an environment variable exists, and set it to the parent directory of where the script is being run if the variable isn't already set. If it is already set, it should do nothing. What's the best way of doing this?
There are two parts. First, the parent of the current working directory is just $PWD/... Second, you can assign a value to a variable if it isn't already set with
: ${MYVAR:=$PWD/..}
The first : is the do-nothing command, but its arguments are still evaluated. The parameter expansion operator := has the side effect of setting MYVAR to the given value if it isn't already set.
Three things:
An environment variable is a globally available, in a program and it child programs. A shell variable is only available in the current shell. You asked to know whether the environment variable exists. Are you sure that is what you want? In that case you may want to do a
if set | grep ^variable_name= > /dev/null ; then
#set the variable
fi
Note that this just checks if the variable exists. If you do it very early in your script, you are almost sure that the variable is indeed an environment variable. But do you really care if it is a variable specific to your local shell or an environment variable?
Second is, do you care if the variable exists? Or do you just want it to contain the value of the parent directory? Do you need the set | grep in the example above, or is it sufficient to test that [ "$variable" != "" ] ? , as in
if [ "$variable" = "" ] ; then
# set the value to the parent-dir
fi
Third, the parent-dir is generally dirname $PWD. $PWD/.. is also the parent-dir.
So, if you do not care whether it is is an environment variable or not, and if you just want it to contain an actual directory, the code would be something like:
if [ "$variable" = "" ] ; then
variable=$(dirname "$PWD")
fi
(which is perhaps a bit more readable Chepner's answer)
Here's my current process:
var[product]=messaging_app
var[component]=sms
var[version]=1.0.7
var[yum_location]=$product/$component/$deliverable_name
var[deliverable_name]=$product-$component-$version
# iterate on associative array indices
for default_var in "${!var[#]}" ; do
# skip variables that have been previously declared
if [[ -z ${!default_var} ]] ; then
# export each index as a variable, setting value to the value for that index in the array
export "$default_var=${var[$default_var]}"
fi
done
The core functionality I'm looking for is to set a list of default variables that will not overwrite previously declared variables.
The above code does that, but it also created the issue of these variables can now not depend on one another. This is because the ordering of the associative array's indices output from "${!var[#]}" is not always the same as the order they were declared in.
Does a simpler solution exist like:
declare --nooverwrite this=that
I haven't been able to find anything akin to that.
Also, I'm aware that this can be done with an if statement. However using a bunch of if statements would kill the readability on a script with near 100 default variables.
From 3.5.3 Shell Parameter Expansion:
${parameter:=word}
If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.
So
: ${this:=that}
: needed because otherwise the shell would see ${this:=that} as a request to run, as a command, whatever that expanded to.
$ echo "$this"
$ : ${this:=that}
$ echo "$this"
that
$ this=foo
$ echo "$this"
foo
$ : ${this:=that}
$ echo "$this"
foo
You can also to this the first place you use the variable (instead of on its own) if that suits things better (but make sure that's clear because it is easy to mess that up in later edits).
$ echo "$this"
$ echo "${this:=that}"
that
$ echo "$this"
that
Doing this dynamically, however, is less easy and may require eval.
This question already has answers here:
What's a concise way to check that environment variables are set in a Unix shell script?
(14 answers)
Closed 9 years ago.
I am writing a shell script, where I have to check if environment variable is set, if not set then I have to set it. Is there any way to check in shell script, whether an environment variable is already set or not ?
The standard solution to conditionally assign a variable (whether in the environment or not) is:
: ${VAR=foo}
That will set VAR to the value "foo" only if it is unset.
To set VAR to "foo" if VAR is unset or the empty string, use:
: ${VAR:=foo}
To put VAR in the environment, follow up with:
export VAR
You can also do export VAR=${VAR-foo} or export VAR=${VAR:=foo}, but some older shells do not support the syntax of assignment and export in the same line. Also, DRY; using the name on both sides of the = operator is unnecessary repetition. (A second line exporting the variable violates the same principal, but feels better.)
Note that it is very difficult in general to determine if a variable is in the environment. Parsing the output of env will not work. Consider:
export foo='
VAR=var-value'
env | grep VAR
Nor does it work to spawn a subshell and test:
sh -c 'echo $VAR'
That would indicate the VAR is set in the subshell, which would be an indicator that VAR is in the environment of the current process, but it may simply be that VAR is set in the initialization of the subshell. Functionally, however, the result is the same as if VAR is in the environment. Fortunately, you do not usually care if VAR is in the environment or not. If you need it there, put it there. If you need it out, take it out.
[ -z "$VARIABLE" ] && VARIABLE="abc"
if env | grep -q ^VARIABLE=
then
echo env variable is already exported
else
echo env variable was not exported, but now it is
export VARIABLE
fi
I want to stress that [ -z $VARIABLE ] is not enough, because you can have VARIABLE but it was not exported. That means that it is not an environment variable at all.
What you want to do is native in bash, it is called parameter substitution:
VARIABLE="${VARIABLE:=abc}"
If VARIABLE is not set, right hand side will be equal to abc. Note that the internal operator := may be replaced with :- which tests if VARIABLE is not set or empty.
if [ -z "$VARIABLE" ]; then
VARIABLE=...
fi
This checks if the length of $VARIABLE is zero.
I'm writing a cPanel postwwwact script, if you're not familiar with the script its run after a new account is created. it relies on the user account variable being passed to the script which i then use for various things (creating databases etc). However, I can't seem to find the right way to access the variable i want. I'm not that good with shell scripts so i'd appreciate some advice. I had read somewhere that the value i wanted would be included in $ARGV{'user'} but this simply gives "root" as opposed to the value i need. I've tried looping through all the arguments (list of arguments here) like this:
#!/bin/sh
for var
do
touch /root/testvars/$var
done
and the value i want is in there, i'm just not sure how to accurately target it. There's info here on doing this with PHP or Perl but i have to do this as a shell script.
EDIT Ideally i would like to be able to call the variable by something other than $1 or $2 etc as this would create issues if an argument is added or removed
..for example in the PHP code here:
function argv2array ($argv) {
$opts = array();
$argv0 = array_shift($argv);
while(count($argv)) {
$key = array_shift($argv);
$value = array_shift($argv);
$opts[$key] = $value;
}
return $opts;
}
// allows you to do the following:
$opts = argv2array($argv);
echo $opts[‘user’];
Any ideas?
The parameters are passed to your script as a hash:
/scripts/$hookname user $user password $password
You can use associative arrays in Bash 4, or in earlier versions of Bash you can use built up variable names.
#!/bin/bash
# Bash >= 4
declare -A argv
for ((i=1;i<=${##};i+=2))
do
argv[${#:i:1}]="${#:$((i+1)):1}"
done
echo ${argv['user']}
Or
#!/bin/bash
# Bash < 4
for ((i=1;i<=${##};i+=2))
do
declare ARGV${#:i:1}="${#:$((i+1)):1}"
done
echo ${!ARGV*} # outputs all variable names that begin with ARGV
echo $ARGVuser
Running either:
$ ./argvtest user dennis password secret
dennis
Note: you can also use shift to step through the arguments, but it's destructive and the methods above leave $# ($1, $2, etc.) in place.
#!/bin/bash
# Bash < 4
# using shift (can use in Bash 4, also)
for ((i=1;i<=${##}+2;i++))
do
declare ARGV$1="$2"
# Bash 4: argv[$1}]="$2"
shift 2
done
echo ${!ARGV*}
echo $ARGVuser
If it's passed as a command-line parameter to the script, it's available as $1 if it's first parameter, $2 for the second, and so on.
Why not start off your script with something like
ARG_USER=$1
ARG_FOO=$2
ARG_BAR=$3
And then later in your script refer to $ARG_USER, $ARG_FOO and $ARG_BAR instead of $1, $2, and $3. That way, if you decide to change the order of arguments, or insert a new argument somewhere other than at the end, there is only one place in your code that you need to update the association between argument order and argument meaning.
You could even do more complex processing of $* to set your $ARG_WHATEVER variables, if it's not always going to be that all of the are specified in the same order every time.
You can do the following:
#!/bin/bash
for var in $argv; do
<do whatver you want with $var>
done
And then, invoke the script as:
$ /path/to/script param1 arg2 item3 item4 etc