0

I wish to execute different commands and check the return code afterwards before moving to the next steps in the script. At the same time I also wish to log the output of the executed commands to a file using the tee command. Example:

#set non-existing folder
local_path="~/njn"
log_path_file="test.log"
cmd="ls -l ${local_path} | tee -a ${log_path_file}";
eval ${cmd}

returncode=$?
echo "execution result: ${returncode}"  | tee -a ${log_path_file};

if [ ${returncode} -eq 0 ]; then
  echo "success"  | tee -a ${log_path_file}
else
  echo "not success"  | tee -a ${log_path_file}
fi

returncode is 0 where it should be > 0

I want the returncode variable to have the actual return of the executed command (in this example, the ls -l command.

I've seen there's a solution using a file to write the output of the command to it and then reading the return code from it (Here), but I'm looking for a more elegant solution.

1

In this particular case, it would be easier to just execute ls -l and act directly on its exit status:

if ls -l "$local_path"; then
    echo 'success'
else
    printf 'failure (code %d)\n' "$?"
fi | tee -a "$log_path_file"

In the bash shell, you may also investigate the values in the PIPESTATUS array:

$ false | true | true | false | false
$ printf '%s\n' "${PIPESTATUS[@]}"
1
0
0
1
1

In your case:

ls -l "$local_path" | tee -a "$log_path_file"

ls_status=${PIPESTATUS[0]}
if [ "$ls_status" -eq 0 ]; then
    echo 'success'
else
    printf 'failure (code %d)\n' "$ls_status"
fi | tee -a "$log_path_file"
  • I did not know you could pipe the exit code of if ... fi directly. | +1 – LinuxSecurityFreak Mar 20 at 8:53
  • @Vlastimil I don't (strictly speaking). I pipe the standard output of ls and of either echo or printf. The exit status of ls is used by if and also in the output of printf (as a string). The actual exit code of the if statement is the exit status of the last command executed by it. In my code, this value is unused. I hope that makes sense... – Kusalananda Mar 20 at 9:00
  • Thank you for the clarification. Have a splendid day! – LinuxSecurityFreak Mar 20 at 9:35
  • 1
    I suggest mentioning PIPESTATUS since the question is tagged bash. – Weijun Zhou Mar 20 at 10:45
  • @WeijunZhou Thanks, added. – Kusalananda Mar 20 at 10:53
0

You could use a function that takes the command as arguments and tee's the output (stdout and stderr) to the logfile.

local_path="~/njn"
log_path_file="test.log"

function log_cmd ()
{
        {
                "$@"
                returncode=$?
                if [ "$returncode" -eq 0 ]; then
                  echo "[successfully executed \"$@\"]"
                else
                  echo "[failed to execute \"$@\", exit code: ${returncode}]"
                fi
                return $returncode

        } 2>&1 | tee -a "$log_path_file"
        # return exit code of first command in pipeline
        return ${PIPESTATUS[0]}
}

log_cmd ls -l "$local_path"
log_cmd echo "hello world"
  • No thing is missing however. the function needs to return the exit code back so that it would be used the determine the next step. Using 'return $?' before the function ends, brings us back to the original problem where it returns the exit code of the tee - 0. If we remove the tee, it works, but not logging to the logfile :/ – T.M Mar 20 at 14:11
  • @T.M Added two return statements to return the exit code of the command before tee. – Freddy Mar 20 at 15:00
0

After some additional testing, I've reached this code switch is wrapped nicely and returns the executed command return code. The code which @Freddy have posted is nearly complete. The return code is exported inside the function but is not exported outside it.

The use of the shopt -s lastpipe was taken from this page: Bash FAQ entry #24: "I set variables in a loop. Why do they suddenly disappear after the loop terminates? Or, why can't I pipe data to read?"

Here is the final working solution:

#!/bin/bash

log_path_file="./logs/test.log"
exe_cmd()
{
    echo "`date +%Y-%m-%d---%r` [Info]: Command to execute: $@"  | tee -a ${log_path_file};
    echo ""  | tee -a ${log_path_file};
    echo ""  | tee -a ${log_path_file};

    set +m
    shopt -s lastpipe

    cmdResult=0
    {
            "$@"
            returncode=$?
            # save result code
            cmdResult=${returncode}

            if [ "$returncode" -eq 0 ]; then
              echo "`date +%Y-%m-%d---%r` [Info]: successfully executed \"$@\""
            else
              echo "`date +%Y-%m-%d---%r` [Info]: failed to execute \"$@\", exit code: ${returncode}"
            fi
    } 2>&1 | tee -a "$log_path_file"

    echo "`date +%Y-%m-%d---%r` [Info]: cmdResult result ${#cmdResult[@]}"

    return ${#cmdResult[@]};
}

cmd="scp some_user@$some_host:some_path/* a_local_path/sub_path";
exe_cmd ${cmd}
returncode=$?
echo "`date +%Y-%m-%d---%r` [Info]: scp execution result: ${returncode}"  | tee -a ${log_path_file};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.