0

I can't get my bash script (a logging file) to detect any other exit code other than 0, so the count for failed commands isn't being incremented, but the successes is incremented regardless of whether the command failed or succeeded.

Here is the code:

#!/bin/bash
#Script for Homework 8
#Created by Greg Kendall on 5/10/2016
file=$$.cmd
signal() {
    rm -f $file
    echo
    echo "User Aborted by Control-C"
    exit
}
trap signal 2
i=0
success=0
fail=0
commands=0
read  -p "$(pwd)$" "command"
while [ "$command" != 'exit' ]
do
    $command
    ((i++))
    echo $i: "$command" >> $file
    if [ "$?" -eq 0 ]
        then
            ((success++))
            ((commands++))
        else
            ((fail++))
            ((commands++))
    fi
    read -p "$(pwd)" "command"
done
if [ "$command" == 'exit' ]
    then
    rm -f $file
    echo commands:$commands "(successes:$success, failures:$fail)"
fi

Any help would be greatly appreciated. Thanks!

8
  • 1
    This is why it's bad practice to check $? when you can just do if $command; then ... and observe exit status directly Commented May 11, 2016 at 2:21
  • 1
    that said, putting your command in a scalar variable is full of bugs too; see mywiki.wooledge.org/BashFAQ/050 Commented May 11, 2016 at 2:21
  • also, == isn't a valid POSIX string comparison operator -- you're thinking of =. The former is supported as an extension in the test builtin provided by some common operating systems and shells, but it's not guaranteed to be present. Commented May 11, 2016 at 2:22
  • @CharlesDuffy: That should be an answer! :) Commented May 11, 2016 at 2:22
  • ...btw, try running this whole thing through shellcheck.net and fixing what it finds. Commented May 11, 2016 at 2:24

2 Answers 2

3

That's because echo $i: "$command" is succeeding always.

The exit status $? in if [ "$?" -eq 0 ] is actually the exit status of echo, the command that is run immediately before the checking.

So do the test immediate after the command:

$command
if [ "$?" -eq 0 ]

and use echo elsewhere

Or if you prefer you don't need the $? check at all, you can run the command and check status within if alone:

if $command; then .....; else ....; fi

If you do not want to get the STDOUT and STDERR:

if $command &>/dev/null; then .....; else ....; fi

** Note that, as @Charles Duffy mentioned in the comment, you should not run command(s) from variables.

Sign up to request clarification or add additional context in comments.

1 Comment

My only concern here is that echoing the OP's use of $command could be taken to endorse this as an acceptable practice. At barest minimum it should be eval "$command" -- that's not good practice, but at least it works (vs the current form, which fails w/ any command having quotes, backslashes, etc).
0

Your code is correctly counting the number of times that the echo $i: "$command" command fails. I presume that you would prefer to count the number of times that $command fails. In that case, replace:

$command
((i++))
echo $i: "$command" >> $file
if [ "$?" -eq 0 ]

With:

$command
code=$?
((i++))
echo $i: "$command" >> $file
if [ "$code" -eq 0 ]

Since $? captures the exit code of the previous command, it should be placed immediately after the command whose code we want to capture.

Improvement

To make sure that the value of $? is captured before any other command is run, Charles Duffy suggests placing the assignment on the same line as the command like so:

$command; code=$?
((i++))
echo $i: "$command" >> $file
if [ "$code" -eq 0 ]

This should make it less likely that any future changes to the code would separate the command from the capture of the value of $?.

5 Comments

Thanks! Just a question though, why does using another variable in place of $? make a difference?
@GregKendall, what makes the difference is that the value of $? is being stored immediately after $command runs, so it's not being changed by your (( i++ )) or your echo.
BTW, I tend to suggest putting the capture on the same line as the command: your_command; code=$? -- that way other commands (like echo) won't be put between the command and capture by someone else adding debug logging or such and not looking too closely.
@CharlesDuffy Excellent suggestion. Answer updated with that change.
Good deal. btw, as I mentioned to heemayl, consider eval "$command" vs $command -- that way we aren't hitting bugs when the command contains quotes, backslashes, or likewise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.