I'm trying to catch a return value from a function in bash, that modify a global variable.
Works perfectly with funtions with no parameters:
#!/bin/bash
tes=2
testfunction(){
tes=3
tes_str="string result"
return 0
}
if output=testfunction; then
echo "OK"
else
echo "KO"
fi
echo $tes
echo $tes_str
But no with parameters:
#!/bin/bash
tes=2
testfunction(){
tes=3
tes_str="string result"
return 0
}
if output=$(testfunction "AA" "BB"); then
echo "OK"
else
echo "KO"
fi
echo $tes
echo $tes_str
Because for bash, parameters ("AA" or "BB") are a command, and I must put it in backets (but if use backets, can't modify global variables).
How can I do it? I'm stucked.
Regards
Why use output? Just remove it and run the function.
if testfunction; then
Notes:
output=testfunction is assigning the text testfunction to the variable output.
output=$(testfunction) will not work, because $(...) runs everything inside a subshell.
Related
I have a bash script that calls a function which returns a value. I have included the scripts below:
Script
source ./utilities/function1.sh
result=$(Function1)
echo "Result: $result"
Function1
function Function1 {
echo "Inside Function: Function1"
cat <<EOF
this is the result
EOF
}
I want to be able to echo to the console within the function and return only the value I want, not including the messages that were echoed to the console, but when I run the script the following is returned:
Result: Inside Function: Func1
this is the result
Is this the best way to return a value from a bash function or is there a way I can echo to the console and return a value without the content of the echo commands from the function?
Thanks in advance
There are a few ways to do what you want. two simple ones are:
Use STDERR to echo to the console and capture STDOUT in your script. By default, STDOUT is on File Descriptor 1 and STDERR is on File Descriptor 2:
function myFunction() {
echo "This goes to STDOUT" >&1 # '>&1' is the default, so can be left out.
echo "This goes to STDERR" >&2
}
result=$(myFunction)
echo ${result}
Use a variable to return a string to the caller:
function myFunction() {
echo "This goes to STDOUT"
result="This goes into the variable"
}
declare result="" # Has global scope. Can be modified from anywhere.
myFunction
echo ${result}
Global scope variables are not good programming practice, but are a necessary evil in bash scripting.
I have a function that runs a set of scripts that set variables, functions, and aliases in the current shell.
reloadVariablesFromScript() {
for script in "${scripts[#]}"; do
. "$script"
done
}
If one of the scripts has an error, I want to exit the script and then exit the function, but not to kill the shell.
reloadVariablesFromScript() {
for script in "${scripts[#]}"; do
{(
set -e
. "$script"
)}
if [[ $? -ne 0 ]]; then
>&2 echo $script failed. Skipping remaining scripts.
return 1
fi
done
}
This would do what I want except it doesn't set the variables in the script whether the script succeeds or fails.
Without the subshell, set -e causes the whole shell to exit, which is undesirable.
Is there a way I can either prevent the called script from continuing on an error without killing the shell or else set/export variables, aliases, and functions from within a subshell?
The following script simulates my problem:
test() {
{(
set -e
export foo=bar
false
echo Should not have gotten here!
export bar=baz
)}
local errorCode=$?
echo foo="'$foo'". It should equal 'bar'.
echo bar="'$bar'". It should not be set.
if [[ $errorCode -ne 0 ]]; then
echo Script failed correctly. Exiting function.
return 1
fi
echo Should not have gotten here!
}
test
If worst comes to worse, since these scripts don't actually edit the filesystem, I can run each script in a subshell, check the exit code, and if it succeeds, run it outside of a subshell.
Note that set -e has a number of surprising behaviors -- relying on it is not universally considered a good idea. That caveat being give, though: We can shuffle environment variables, aliases, and shell functions out as text:
envTest() {
local errorCode newVars
newVars=$(
set -e
{
export foo=bar
false
echo Should not have gotten here!
export bar=baz
} >&2
# print generate code which, when eval'd, recreates our functions and variables
declare -p | egrep -v '^declare -[^[:space:]]*r'
declare -f
alias -p
); errorCode=$?
if (( errorCode == 0 )); then
eval "$newVars"
fi
printf 'foo=%q. It should equal %q\n' "$foo" "bar"
printf 'bar=%q. It should not be set.\n' "$bar"
if [[ $errorCode -ne 0 ]]; then
echo 'Script failed correctly. Exiting function.'
return 1
fi
echo 'Should not have gotten here!'
}
envTest
Note that this code only evaluates either export should the entire script segment succeed; the question text and comments appear to indicate that this is acceptable if not desired.
I have small script which have a function asking user input (name of user) and then I have echo function
which is running the function and asking for input,
After that I have echo the $User_name in last line (Users_name_is - )which is set in function but its result is black, I want to use $User_name in further script.
what i am doing wrong ?
#!/bin/sh
funtion_one()
{
read varname
if [ $varname == skull ]; then
echo "Nice to meet you $varname"
#User_name=$varname
else
echo "I dont know you $varname"
fi
User_name=$varname
}
echo Hello, who am I talking to?
while :
do
case $(funtion_one) in
"Nice to meet you skull") break
;;
"I dont know you") $(funtion_one)
;;
esac
done
echo "Users_name_is - $User_name"
I want result Users_name_is - skull
When you do $(funtion_one), you are executing the function inside a subshell, so any variables created cease to exist after the function finishes.
An alternative would be this:
function_one()
{
read varname
if [ "$varname" = skull ]; then
echo "Nice to meet you $varname" >&2
else
echo "I dont know you $varname" >&2
fi
echo "$varname"
}
user_name=$(function_one)
Now user_name exists in the parent shell. The messages are sent to standard error, and the name that has been read is sent to standard output so that it can be captured by the command substitution $().
Alternatively, you can simply execute the function in the parent shell:
# change
echo $(funtion_one)
# to
funtion_one
But then all the variables used inside the function will continue to exist after it has been run.
It seems the function is getting in the way of you achieving what you want. I would restructure your code to something much simpler like this:
while read name; do
if [ "$name" = skull ]; then
echo "Nice to meet you $name"
break
fi
echo "I don't know you $name"
done
There are a lot of ways to structure your code, and it seems like you're trying to do something like:
#!/bin/sh
get_user_name() {
local varname
printf 'Hello, who am I talking to? '
read varname
if test "$varname" = skull; then
echo "Nice to meet you $varname"
User_name=$varname
return 0
else
echo "I dont know you $varname" >&2
return 1
fi
}
unset User_name
while ! get_user_name
do
case "$User_name" in
skull) break
;;
esac
done
echo "Users_name_is - $User_name"
It's perfectly valid to use a function to get the input, but if you want that function to set a variable in the caller you cannot call it as a subshell, and it's easiest if the shell returns a value to indicate success or failure.
In function_two, I need to get both the output from echo and the return value from function_one
#!/bin/bash
function_one() {
echo "okay then"
return 2
}
function_two() {
local a_function="function_one"
local string=`${a_function}`
echo $string # echos "okay then"
echo "$?" #echos 0 - how do we get the returned value of 2?
}
function_two
When trying echo "$?" I get 0 instead of 2
Update
As Ipor Sircer pointed out, $? above is giving the exit code of the previous command echo $string
So instead I grab the exit code immediately after. And as choroba mentioned, the localization and assignment of the variable needed to be separated.
Here is the working script:
#!/bin/bash
function_one() {
echo "okay then"
return 2
}
function_two() {
local a_function="function_one"
local string
string=`${a_function}`
local exitcode=$?
echo "string: $string" # okay then
echo "exitcode: $exitcode" # 2
}
function_two
0 is the exit status of the last command executed, i.e. echo $string.
If you need to use the exit status later, store it in a variable:
local string
string=`${a_function}`
local status=$?
echo "Output: $string"
echo "Status: $status"
You also need to separate the assignment and localization of the variable to not get the status of local instead.
I have two shell script like as follows:
a.sh
tes=2
testfunction(){
tes=3
echo 5
}
testfunction
echo $tes
b.sh
tes=2
testfunction(){
tes=3
echo 5
}
val=$(testfunction)
echo $tes
echo $val
In first script tes value is '3' as expected but in second it's 2?
Why is it behaving like this?
Is $(funcall) creating a new sub shell and executing the function? If yes, how can address this?
$() and `` create new shell and return output as a result.
Use 2 variables:
tes=2
testfunction(){
tes=3
tes_str="string result"
}
testfunction
echo $tes
echo $tes_str
output
3
string result
Your current solution creates a subshell which will have its own variable that will be destroyed when it is terminated.
One way to counter this is to pass tes as a parameter, and then return* it using echo.
tes=2
testfunction(){
echo $1
}
val=$(testfunction $tes)
echo $tes
echo $val
You can also use the return command although i would advise against this as it is supposed to be used to for return codes, and as such only ranges from 0-255.Anything outside of that range will become 0
To return a string do the same thing
tes="i am a string"
testfunction(){
echo "$1 from in the function"
}
val=$(testfunction "$tes")
echo $tes
echo $val
Output
i am a string
i am a string from in the function
*Doesnt really return it, it just sends it to STDOUT in the subshell which is then assigned to val