exit form shell script not working as expected - bash

I have a function that is called twice with 2 different parameters. The function performs some checks and exits execution if check fails. If check succeeds, the execution continues. However in my case the execution is not exited, and it continues to call the function a second time.
I am calling the function each time and storing the returned value in variables. From what I have understood looking through answers on google and stackoverflow, the problem seems to be the fact that when I am calling the function to store it in the variable, it is running in a subshell and the "exit" just exits execution from the subshell, while the shell script continues executing. I am providing the code below:
check_profile_path() {
local profileToCheck=$1
if [ -e "$pdfToolBoxPath/used_profiles/Check$profileToCheck.kfpx" ]; then
return 0
else
outputArr[status]="failed"
outputArr[message]="profile configuration path for $profileToCheck not found at specified path"
exitCode=1
end_execution
fi
}
check_profile() {
local profileName=$1
check_profile_path $profileName
local containedText=$($someApplicationPath $somePath/used_profiles/Check$profileName.kfpx $fileToCheck)
echo "<<<<<<<<<< $profileName >>>>>>>>>>"
echo "$containedText"
echo ""
}
end_execution() {
jsonResult=$(create_json)
echo $jsonResult
exit $exitCode
}
colorSpaceProfileName="ColorSpace"
resolutionProfileName="Resolution"
colorSpaceCheckResult=$(check_profile $colorSpaceProfileName)
echo "$colorSpaceCheckResult"
resolutionCheckResult=$(check_profile $resolutionProfileName)
echo "$resolutionCheckResult"
The output I recieve from this is:
{"status":"failed","message":"profile configuration path for ColorSpace not found at specified path"}
<<<<<<<<<< Resolution >>>>>>>>>>
ProcessID 8..........
while I expect it to be just:
{"status":"failed","message":"profile configuration path for ColorSpace not found at specified path"}
I cannot set the proper syntax.. Please suggest..

With exit, the current process is ended. You are invoking your functions as, i.e., $(check_profile $colorSpaceProfileName), which means that they are run into their own process, and hence the exit inside the function only leaves this process.
Here are two workarounds:
Don't collect the output of the function in this way. Collect them inside the function and store them into a variable, which you can then retrieve on the calling side.
Arrange that the functions set a exit code, depending on whether the caller should exit or not, and evaluate the exit code of the function at the calling side, i.e. something like:
colorSpaceCheckResult=$(check_profile $colorSpaceProfileName)
(( $? == 2 )) && exit

Related

Tcsh Script Last Exit Code ($?) value is resetting

I am running the following script using tcsh. In my while loop, I'm running a C++ program that I created and will return a different exit code depending on certain things. While it returns an exit code of 0, I want the script to increment counter and run the program again.
#!/bin/tcsh
echo "Starting the script."
set counter = 0
while ($? == 0)
# counter ++
./auto $counter
end
I have verified that my program is definitely returning with exit code = 1 after a certain point. However, the condition in the while loop keeps evaluating to true for some reason and running.
I found that if I stick the following line at the end of my loop and then replace the condition check in the while loop with this new variable, it works fine.
while ($return_code == 0)
# counter ++
./auto $counter
set return_code = $?
end
Why is it that I can't just use $? directly? Is another operation underneath the hood performed in between running my custom program and checking the loop condition that's causing $? to change value?
That is peculiar.
I've altered your example to something that I think illustrates the issue more clearly. (Note that $? is an alias for $status.)
#!/bin/tcsh -f
foreach i (1 2 3)
false
# echo false status=$status
end
echo Done status=$status
The output is
Done status=0
If I uncomment the echo command in the loop, the output is:
false status=1
false status=1
false status=1
Done status=0
(Of course the echo in the loop would break the logic anyway, because the echo command completes successfully and sets $status to zero.)
I think what's happening is that the end that terminates the loop is executed as a statement, and it sets $status ($?) to 0.
I see the same behavior with both tcsh and bsd-csh.
Saving the value of $status in another variable immediately after the command is a good workaround -- and arguably just a better way of doing it, since $status is extremely fragile, and will almost literally be clobbered if you look at it.
Note that I've add a -f option to the #! line. This prevents tcsh from sourcing your init file(s) (.cshrc or .tcshrc) and is considered good practice. (That's not the case for sh/bash/ksh/zsh, which assign a completely different meaning to -f.)
A digression: I used tcsh regularly for many years, both as my interactive login shell and for scripting. I would not have anticipated that end would set $status. This is not the first time I've had to find out how tcsh or csh behaves by trial and error and been surprised by the result. It is one of the reasons I switched to bash for interactive and scripting use. I won't tell you to do the same, but you might want to read Tom Christiansen's classic "csh.whynot".
Slightly shorter/simpler explanation:
Recall that with tcsh/csh EACH command (including shell builtin) return a status. Therefore $? (aliases to $status) is updated by 'if' statements, 'for' loops, assignments, ...
From practical point of view, better to limit the usage of direct use of $? to an if statement after the command execution:
do-something
if ( $status == 0 )
...
endif
In all other cases, capture the status in a variable, and use only that variable
do-something
something_status=$?
if ( $something_status == 0 )
...
endif
To expand on the $status, even a condition test in an if statement will modify the status, therefore the following repeated test on $status will not never hit the '$status == 5', even when do-something will return status of 5
do-something
if ( $status == 2 ) then
echo FOO
else if ( $status == 5 ) then
echo BAR
endif

How to avoid calling external utility (grep) twice while maintaining return code & output?

I have following Bash function which return property value from Java-style property file. It property wasn't found, it should return non-zero. If found, property's value will be printed & return code must be zero.
function property_get() {
local pfile="$1"
local pname="$2"
if egrep "^${pname}=" "$pfile" 2>&1 >/dev/null; then
local line="$(egrep "^${pname}=" "$pfile")"
printf "${line#*=}"
return 0 # success
else
return 1 # property not found
fi
}
The question is: how to avoid from calling egrep twice? First exec is for status code, 2nd is for property value. If I use $(grep parameters) notation, then grep will be launched in subshell and I can't get it's return code and won't be able to determine success or failure of property searching.
This should work:
...
local line
if line=$(egrep "^${pname}=" "$pfile" 2>/dev/null); then
...
As #matt points out, you can just get the line then check the exit status of your command:
line=$(...)
if test $? -eq 0; then
# success
...
else
# property not found
...
fi
Edit:
To summarize:
You can use var=$(cmd), to set var to the standard output of cmd and $? to the exit status of cmd
With local var=$(cmd), $? will be set to the exit status of local which is 0 as explained here: Why does "local" sweep the return code of a command?

Where does the exit status go after trap/return?

I was playing around with using trap inside a function because of this question, and came up with this secondary question. Given the following code:
d() {
trap 'return' ERR
false
echo hi
}
If I run d, the trap causes the shell to return from the function without printing 'hi'. So far so good. But if I run it a second time, I get a message from the shell:
-bash: return: can only `return' from a function or sourced script
At first, I assumed this meant the ERR sig was happening twice: Once when false gave a nonzero exit status (inside the function) and again when the function itself returned with a nonzero exit status (outside the function). But that hypothesis doesn't hold up against this test:
e() {
trap 'echo +;return' ERR
false
echo hi
}
If I run the above, no matter how often I run it, I no longer get the can only return from a function or sourced script warning from bash. Why does the shell treat a compound command different from a simple command in a trap arg?
My goal was to maintain the actual exit status of the command that caused the function to exit, but I think whatever is causing the above behavior also makes capturing the exit status complicated:
f() {
trap '
local s=$?
echo $s
return $s' ERR
false
echo hi
}
bash> f; echo $?
1
0
Wat? Can someone please explain why $s expands to two different values here and, if it turns out to be the same cause, the above differentiation between return and echo +; return?
Your first conclusion was right: the ERR sig was happening twice.
During the first execution of 'd', you define a trap globally. This affect next commands (the current call of d is not affected).
During the second execution of 'd', you define a trap again (not very useful), the call of 'false' fails, so we execute the handler defined by the trap. Then we return in the parent shell where 'd' fails too, so we execute the trap again.
Just a remark. ERR can be given as 'sigspec' argument, but ERR is not a signal ;-) From the manual of BASH:
If a sigspec is ERR, the command arg is executed whenever a sim‐
ple command has a non-zero exit status, subject to the following
conditions. [...]
These are the same conditions obeyed by the errexit option.
With the function 'e', the ERR handler executes the 'echo' command that succeeds. That's why the 'e' function doesn't fail, that's why the ERR handler is not called twice in this case.
If you try "e; echo $?" you will read "0".
Then I tried your 'f' function. I observed the same behavior (and I was surprised). The cause is NOT a bad expension of "$s". If you try to hardcode a value, you should observe the argument given to the 'return' statement is ignored when it is executed in by the trap handler.
I don't know if it is a normal behavior or if it's a bug of BASH... Or maybe a trick to avoid an infinite loop in the interpreter :-)
By the way, it's not a good use of trap in my opinion. We can avoid side effect of trap by creating a sub-shell. In this case we avoid the error in the parent shell and we keep the exitcode of the inner function:
g() (
trap 'return' ERR
false
echo hi
)

Stop processing sourced file and continue

When writing bash that sources another file, sometimes I want to skip processing if some conditions are true. For now, I've been either:
Wrapping the entire sub file in nested if statements
Wrapping the call to the source file in nested if statements
Both of these strategies have some drawbacks. It'd be so much better if I could write my scripts with this code style:
main.sh
echo "before"
. other
echo "after"
other.sh
# guard
if true; then
# !! return to main somehow
fi
if true; then
# !! return to main somehow
fi
# commands
echo "should never get here"
Is there a way to do this so that the echo "after" line in main gets called?
Yes, you could return:
if true; then
return
fi
Quoting help return:
return: return [n]
Return from a shell function.
Causes a function or sourced script to exit with the return value
specified by N. If N is omitted, the return status is that of the
last command executed within the function or script.
Exit Status:
Returns N, or failure if the shell is not executing a function or script.

What's does a bash function return when there is no “return” statement?

Is the return value of a bash function the status of the last executed command?
I wrote this test and it looks like it's so. I just want to verify. No one has asked this question before apparently and tutorials don't mention this.
Test program:
funa() {
echo "in funa";
true;
};
funb() {
echo "in funb"
false;
};
funa && echo "funa is true";
funb && echo "funb is true";
Output when I run the program:
in funa
funa is true
in funb
Does anyone know the answer?
Yes. Per man bash:
Shell Function Definitions
When executed, the exit status of
a function is the exit status of the last command executed in
the body. (See FUNCTIONS below.)
Did you try reading the manpage? It's in there.
When executed, the exit status of
a function is the exit status of the last command executed in
the body. (See FUNCTIONS below.)

Resources