KSH: Confused with behaviour of exit

Hi Everyone,
I am confused on why the below snippet of code is not working as I intend it to do. I have googled and confirmed that "exit" is supposed to abort the execution of the script regardless if the exit was called from inside a function, or the main body of the script.

log_and_die() {
        # Print the error message and terminate the execution of the script
        typeset __MESSAGE_TYPE__="FATAL"
        typeset __MESSAGES__="$1"
        print "$__MESSAGES__" | while read line
        do
                write_log "$__MESSAGE_TYPE__" "$line"
        done
        exit 1
}
...
get_db_password() {
        #-- requires the database user as an argument
        if [ $# -ne 1 ]; then
                log_and_die "The function \"get_db_password()\" requires the username as an argument."
        fi
        typeset L_DB_USER="$1"
        typeset L_DB_PASS=""
        if [ ! -e $APP_CONFIG_DIR/$L_DB_USER.pub ]; then
                log_and_die "The public key \"$L_DB_USER.pub\" was not found in the config directory."
        fi
        if [ ! -e $APP_CONFIG_DIR/$L_DB_USER.key ]; then
                log_and_die "The private key \"$L_DB_USER.key\" was not found in the config directory."
        fi
        L_DB_PASS=$(decrypt -a aes -i $APP_CONFIG_DIR/$L_DB_USER.key -k $APP_CONFIG_DIR/$L_DB_USER.pub)
        if [ $? -ne 0 ]; then
                log_and_die "There was an error in decrypting the public / private key pair for $L_DB_USER."
        fi
 
        print "$L_DB_PASS"
}
...
run_query(){
        #-- Will take the following arguments
        #       1. username
        #       2. password
        #       3. database instance
        #       4. sql query to execute
        #       5. output destination
 if [ $# -ne 5 ]; then
                log_and_die "The function \"run_query()\" requires the following arguments: (1) username, (2) password, \
(3) db instance, (4) sql query file, (5) output file."
        fi
        typeset L_DB_USER="$1"
        typeset L_DB_PASS="$2"
        typeset L_DB_INSTANCE="$3"
        typeset L_SQL_QUERY="$4"
        typeset L_OUTPUT_FILE="$5"
        typeset L_OUTPUT
        #-- Perform validation on the arguments
        #-- Check if the database instance is live
        L_OUTPUT=`tnsping $L_DB_INSTANCE 2>&1`
        if [ $? -ne 0 ]; then
                print "$L_OUTPUT" | while read line
                do
                        log_warn "$line"
                done
                log_and_die "Unable to ping the database instance \"$L_DB_INSTANCE\"."
        fi
        #-- Check if the SQL query exists.
        if [ ! -e $L_SQL_QUERY ]; then
                log_and_die "Unable to find the sql file \"$L_SQL_QUERY\"."
        fi
L_OUTPUT=`sqlplus -s $L_DB_USER/$L_DB_PASS@$L_DB_INSTANCE <<EOF 2>&1
WHENEVER OSERROR EXIT FAILURE
WHENEVER SQLERROR EXIT FAILURE
SPOOL $L_OUTPUT_FILE
@$L_SQL_QUERY
EXIT
EOF`
        if [ $? -ne 0 ]; then
                print "$L_OUTPUT" | while read line
                do
                        log_warn "$line"
                done
                log_and_die "Errors were encountered while trying to execute the query \"$L_SQL_QUERY\"."
        fi
 
}

Using the below statement does not abort the script. Please take note that the file "ITM2.pub" does not exists so I am expecting the whole script to abort once it goes thru the function get_db_password().

run_query "ITM" "$(get_db_password ITM2)" "AUWMRCD2A" "$SQL_QUERY" "$REPORT"

-- or --

DB_PASS="$(get_db_password ITM2)"
...
run_query "ITM" "$DB_PASS" "AUWMRCD2A" "$SQL_QUERY" "$REPORT"

However, I tried the below statement and it worked fine meaning that the function get_db_password() and log_and_die() is properly working.

get_db_password ITM2

My question is, if you use a function as a parameter or assign the output of the function to a variable, will it make "exit" behave differently? If the answer is yes, is there any workaround available?

By using $(get_db_password ITM2), the function is run inside a sub-shell. The "exit 1" exits this sub-shell and then it is back to the parent shell, where you can check the sub-shell's return code, which will be 1 and undertake appropriate action..

For example:

if ! DB_PASS="$(get_db_password ITM2)" ; then
  exit 1
fi
1 Like

or the simpler:

DB_PASS="$(get_db_password ITM2)" || exit 1
1 Like

Thanks for your replies guys. Actually I arrived at the same solution as what you have given. I guess I am making this too complicated that what it is supposed to be (not a native shell programmer).

DB_USER="ITM"
DB_PASS="$(get_user_pass $DB_USER)" || log_and_die "Failed to get the password for the user \"$DB_USER\"."