Update value of a variable from a function in shell script - shell

a=master
b="9876"
secondfunction(){
a=develop
b="1234"
echo "Inside second function"
echo $1
echo $2
}
secondfunction $a $b
In the above shell script echo command prints a=master and b=9876. I want to print a=develop and b =1234.

Here you are indeed changing the value of a and b like you want to, To see this you can print out a and b after the function has been called
echo $a $b
secondfunction $a $b
echo $a $b
and you can see that the output will be master 9876 for the first one and develop 1234 for the second one. Just that you cannot change the value of $1 and $2 by changing the values of a and b. For that you'll need to explicitly change them using set like #william has pointed out.
Also variables in shell script are globally scoped unless explicitly mentioned otherwise. So if all you want to do is change the values of those variables, you need not explicitly pass them into the function as arguments.
a=master
b="9876"
secondfunction(){
a=develop
b="1234"
echo "Inside second function"
echo $a
echo $b
}
secondfunction
will also work

Related

Setting numeric variables in a script ("set $i 1") not working ("echo $i" empty)

As the title says I need help with a bash script that has to generate a string given two numeric variables. That string will be used to generate a file name, but when I test the name generation code it yields nothing
The script is the code that follows
# !usr/bin
set nombre
declare -a i
declare -a j
set $i 1
set $j 2
set nombre "$i\_$j.txt"
echo $i
echo $j
Here is what it yields:
entropy#3PY:~$ ./test4
As you can see it yields nothing while it should yield
1
2
thanks in advance
set is not used to assign values to regular variables; it is used to set the values of the positional parameters or to modify shell options. You need a regular assignment.
i=1
j=2
nombre="${i}_$j.txt"
echo "$i"
echo "$j"
echo "$nombre"
There is no need to declare variables prior to assignment; assignment creates a variable if necessary. The declare command is more about setting attributes on a name (-a, for instance, would mark the variable as an array variable, something you do not need here).
As an example of how set does work, consider
echo "First positional parameter: $1"
echo "Second positional parameter: $2"
set foo bar # $1=foo, $2=bar
echo "First positional parameter: $1"
echo "Second positional parameter: $2"
Does this code works as desired?
#!/bin/bash
i=1
j=2
echo $i
echo $j
nombre=$i"_"$j.txt
echo $nombre

Overlapped data when amend values of variables in shell script ubuntu 9.04 for ARM

I created a shell script in /etc/init.d/ to start/stop my application. The script is based on a skeleton script file which was originally located in /etc/init.d. This script imports variables from the xxx.conf configuration file.
# Read configuration variable file if it is present
[ -r /etc/xxx.conf ] && . /etc/xxx.conf
When assigning each imported variable and printing out the value with echo, it works well.
local A
A=$imported1
#print out exactly actual value of imported1
echo $A
local B
B=$imported2
#print out exactly actual value of imported1
echo $B
local C
C=$imported3
#print out exactly actual value of imported3
echo $C
However, when amending A, B, and C into a new string or even amending imported1, imported2, and imported3 into a new string, these values will overlap each other.
local D
D="$A $B $C"
#print out value is overlap string of A,B and C instead of 'A B C'
echo $D
D="$imported1 $imported2 $imported3"
#print out value is overlap string of imported1 ,imported2 and
#imported3,as same as result when do echo "$A $B $C"
echo $D
How can I amend these imported variables correctly?
I suspect you might have some control characters hidden in your variables. Probably a carriage return, which moves the cursor to the start of the line when you print it to a terminal.
You can make a carriage return many different ways -- this example will use echo -e "\r":
A="$(echo -en 'A LONG WORD\r')"
B="$(echo -en 'short\r')"
Just like in your example, printing them individually will look like they only contain letters:
$ echo $A
A LONG WORD
$ echo $B
shorter
But if you print them together, the later one overlaps the earlier one:
$ echo "$A $B"
short WORD
You can see the carriage return if you look at the output with less, od -a, or anything that doesn't interpret that control character to move the cursor.

In a function Bash: how to check if an argument is a set variable?

I want to implement a bash function which test is the 1st argument is actually a variable, defined somewhere.
For instance, in my .bashrc :
customPrompt='yes';
syntaxOn='no';
[...]
function my_func {
[...]
# I want to test if the string $1 is the name of a variable defined up above
# so something like:
if [[ $$1 == 'yes' ]];then
echo "$1 is set to yes";
else
echo "$1 is not set or != to yes";
fi
# but of course $$1 doesn't work
}
output needed :
$ my_func customPrompt
> customPrompt is set to yes
$ my_func syntaxOn
> syntaxOn is set but != to yes
$ my_func foobar
> foobar is not set
I tried a lot of test, like -v "$1", -z "$1", -n "$1", but all of them test $1 as a string not as a variable.
(please correct me if I make not myself clear enought)
In the bash you can use the indirect variable subtituion.
t1=some
t2=yes
fufu() {
case "${!1}" in
yes) echo "$1: set to yes. Value: ${!1}";;
'') echo "$1: not set. Value: ${!1:-UNDEF}";;
*) echo "$1: set to something other than yes. Value: ${!1}";;
esac
}
fufu t1
fufu t2
fufu t3
prints
t1: set to something other than yes. Value: some
t2: set to yes. Value: yes
t3: not set. Value: UNDEF
The ${!variablename} in bash mean indirect variable expansion. Described in the e.g. https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
Whrere:
The basic form of parameter expansion is ${parameter}. The value of
parameter is substituted. The braces are required when parameter is a
positional parameter with more than one digit, or when parameter is
followed by a character that is not to be interpreted as part of its
name.
If the first character of parameter is an exclamation point (!), a
level of variable indirection is introduced. Bash uses the value of
the variable formed from the rest of parameter as the name of the
variable; this variable is then expanded and that value is used in the
rest of the substitution, rather than the value of parameter itself.
This is known as indirect expansion. The exceptions to this are the
expansions of ${!prefix } and ${!name[#]} described below. The
exclamation point must immediately follow the left brace in order to
introduce indirection.
Also, check this: https://stackoverflow.com/a/16131829/632407 how to modify in a function a value of the variable passed indirectly.
You can check variable set or not by simply like
if [[ $var ]]
then
echo "Sorry First set variable"
else
echo $var
fi
You can do something like this for your script
customPrompt='yes';
syntaxOn='no';
function my_func
{
if [[ ${!1} ]];then
echo "$1 is set to ${!1}";
else
echo "$1 is not set";
fi
}
my_func customPrompt
my_func syntaxOn
my_func foobar
Output:
customPrompt is set to yes
syntaxOn is set to no
foobar is not set
You can customize the function as per you requirement by simply making some comparison conditions.
For more details you can check this answer
If you really want to check if your variable is set or unset (not just empty), use this format:
function my_func {
if [[ -z ${!1+.} ]]; then
echo "$1 is not set."
elif [[ ${!1} == yes ]]; then
echo "$1 is set to yes"
else
echo "$1 is set to \"${!1}\"."
fi
}
You're going to have problems...
The Bash shell is a very wily creature. Before you execute anything, Bash comes in and interpolates your command. Your command or shell script never sees whether or not you have a variable as a parameter.
$ set -x
set -x
$ foo=bar
+ foo=bar
$ echo "$foo"
+ echo bar
bar
$ set +x
The set -x turns on debugging mode in the shell. It shows you what a command actually executes. For example, I set foo=bar and then do echo $foo. My echo command doesn't see $foo. Instead, before echo executes, it interpolates $foo with bar. All echo sees at this point is that it's suppose to take bar as its argument (not $foo).
This is awesomely powerful. It means that your program doesn't have to sit there and interpret the command line. If you typed echo *.txt, echo doesn't have to expand *.txt because the shell has already done the dirty work.
For example, here's a test shell script:
#! /bin/sh
if [[ $1 = "*" ]]
then
echo "The first argument was '*'"
else
"I was passed in $# parameters"
fi
Now, I'll run my shell script:
$ test.sh *
I was passed in 24 parameters
What? Wasn't the first parameter of my script a *? No. The shell grabbed * and expanded it to be all of the files and directories in my directory. My shell script never saw the *. However, I can do this:
$ test.sh '*'
The first argument was '*'
The single quotes tell the shell not to interpolate anything. (Double quotes prevent globbing, but still allow for environment variable expansion).
This if I wanted to see if my first parameter is a variable, I have to pass it in single quotes:
$ test.sh '$foo'
And, I can do this as a test:
if [[ $1 != ${1#$} ]]
then
echo "The first parameter is the variable '$1'"
fi
The ${1#$} looks a bit strange, but it's just ${var#pattern}. This removes pattern from the left most side of $var. I am taking $1 and removing the $ if it exists. This gets expanded in the shell as:
if [[ $foo != foo ]]
which is true.
So, several things:
First, you've got to stop the shell from interpolating your variable. That means you have to use single quotes around the name.
You have to use pattern matching to verify that the first parameter starts with a $.
Once you do that, you should be able to use your variable with ${$1} in your script.

Bash indirect variable assignment inside a function

I have a script where the user input needs to be evaluated several times, the solution im working on is to put the evaluation bits into a function, and simply call the function every time i need to evaluate the input.
The problem is though that when im trying to update the $1 variable (that referes to the first variable parameter of the function) I get the error message "$VARIABLE command not found".
Here is the code:
function input_handler() {
if is_integer $1; then
selid="$1 -1"
if [[ "$1" -le "0" ]]; then
echo "Please use a simple positive number!"
else
if [[ "$1" -le "${#array[*]}" ]]; then
eval $1="${array[selid]}"
echo "Ok, moving on..."
else
echo "That number seems too large, try again?"
fi
fi
else
if [ -e $2/$1 ]; then
echo "Ok, moving on..."
else
echo "That item is not on the list, try again!"
fi
fi
}
And this command:
input_handler $doctype $docpath
Gives this output:
5
./test: line 38: 5=sun: command not found
Ok, moving on...
Now this is almost correct, but what im after is doctype=sun, not 5=sun, in other words I need the $1 variable name not its value. Changing the line eval $1="${array[selid]}" to eval doctype="${array[selid]}" fixes this particular instance. But this does not fix my problem as I need to run this function on different variables with different names.
Maybe not fully understand what you want achieve, but check the next example:
weirdfunc () {
echo " weirdfunc: variable name is: $1"
echo " weirdfunc: variable value is: ${!1}"
eval "$1=$(( ${!1} + 1))" #assign
}
myvar="5"
echo "the value of myvar before: $myvar"
weirdfunc myvar #call with the NAME not with the value, so NOT weridfunc $myvar
echo "the value of myvar after: $myvar"
In short - when you want to do anything with the variable NAME in an called function, you should pass the NAME of the variable and NOT his value. So call the function
somefunc NAME
instead of
somefunc $NAME
and use the above constructs to get the name and value inside the function.
You can't update the value of $1 with a traditional assignment, but you can update the positional parameters with the set builtin.
$ f() { echo "$#"; set -- a b c; echo "$#"; echo $2; }
$ f 1 2 3
1 2 3
a b c
b
Just keep in mind this will wipe out all the positional parameters you don't re-set each time, so you'll need to set $2 if you want to keep it around.
Your best bet is probably to assign the values in the positional parameters to names and just use names from then on.
If you protect the variable name, Bash will evaluate and assign to $1 instead of try to execute $1=value.
eval "$1"=${array[selid]}
Positional parameters are read-only. So what you want to do is not possible. You should do something like
foo=$1
and then work with $foo instead of $1

bash expanding value of a variable to use in another variable

I hope the question makes sense, I would like to do something like:
a=test
b=a
echo ${$b} # should echo test
Basically I'd like $b to expand to the value a and have bash echo out the value of variable $a (since ${a} is test).
What syntax should I use?
a=test
b=a
echo ${!b} # does echo test

Resources