2

I'm currently refactoring a script which has slowly grown beyond control. I'm trying to spin off repetition into functions. However, I have a repeated test being called from a loop, and want it to return continue.

Shellcheck says

SC2104: In functions, use `return` instead of `continue`.

And the shellcheck wiki says don't do it. But is there a way?

Below is an example:

#!/bin/sh

AFunction () {
    if [[ "${RED}" -lt 3 ]]; then
        echo "cont"
        continue
    else
        echo "nope"
    fi
}

for i in 1 2 3 4 5
do
    RED=${i}
    AFunction
    echo ${i}
done

This is the output:

cont
1
cont
2
nope
3
nope
4
nope
5

But I would expect

cont
cont
nope
3
nope
4
nope
5

Thanks everyone for the answers so far. I'm close but now have a spin off question. Hopefully that's ok?

If I use a combination of @sudodus answer and @alecxs tips. Do I need to then always "return" a 0 at the end of the function? Seems like good practice now, but is it implied if I don't explicitly do it?

#!/bin/sh

AFunction () {
    ##Script doing loads of other stuff
    if [[ "${RED}" -lt 3 ]]; then
        echo "cont"
        ## This only happening because something has gone wrong
        return 1
    else
        echo "nope"
    fi
    ##Script doing loads of more stuff
}

for i in 1 2 3 4 5
do
    RED=${i}
    AFunction || continue
    echo ${i}
done
5
  • 1
    Exactly how are you running this? If I run the contents of the function in bash (like you tagged), it throws 'continue: only meaningful in a ... loop'. If I run it in sh (like your shebang) it throws 'sh: 1: [[: not found'. I think your /bin/sh is a link to ksh. Jul 30 '20 at 15:22
  • Hi @alecxs, so the problem is I want to return the command to continue from the function to be used in the loop.
    – Stripy42
    Jul 30 '20 at 15:26
  • @Paul_Pedant, I would not be surprised if the shell behaves a bit odd, it's ancient. It needs to run on SUSE 11.0. Patching these old boxes up to 11.4 is another task.
    – Stripy42
    Jul 30 '20 at 15:28
  • See austingroupbugs.net/view.php?id=842 and austingroupbugs.net/view.php?id=1058 for what POSIX has to say about that. Jul 30 '20 at 16:06
  • 1
    yes, to be safe usually return 0 is placed at end of function. in other cases it might useful to return exit code from child process via variable, that way you can check the success of other commands (like rsync) but in your case just 0 and 1 is sufficient unix.stackexchange.com/q/110348
    – alecxs
    Jul 30 '20 at 16:28
3

You can use 'return` with a parameter for example like the following,

#!/bin/bash

AFunction () {
    if [[ "${RED}" -lt 3 ]]; then
        echo "cont"
        return 1
    else
        echo "nope"
        return 0
    fi
}

for i in 1 2 3 4 5
do
    RED=${i}
    if AFunction
    then
    echo ${i}
    fi
done
8
  • Hi @sudodus, this is a lot cleaner than what the shellcheck wiki says. I'll just test it now and see if it works in the main script.
    – Stripy42
    Jul 30 '20 at 15:31
  • Thanks @alecxs, I think that's what I need. @sudodus If you just do AFunction || continue will that work?
    – Stripy42
    Jul 30 '20 at 15:46
  • 1
    @sudodus continue is used in loops to break the current iteration without processing code (like break does end the entire loop)
    – alecxs
    Jul 30 '20 at 15:56
  • 1
    @Stripy42, alecxs is right about the usage of continue. My question was more about why you prefer it to the ìf statement. What you want it is perform some action depending of some condition, and to continue the loop. If the function might break the loop, I think it would make a difference. Use the syntax that works best and/or gives the cleanest syntax. -- What kind of 'continue' is it? How is it sent from the function in the 'big script'? And what is sent otherwise (nothing or something else)?
    – sudodus
    Jul 30 '20 at 16:12
  • 1
    Hi, I'm now using the fix you've provided (thanks!) to replace a lot of repetitive tasks I've taken out of what was a single loop into functions. So I'll likely use both option, the short && + || and the IF ELSE FI option, depending on what the functions do.
    – Stripy42
    Jul 30 '20 at 16:25
0
#!/bin/sh

AFunction () {
    [ "${RED}" -lt 3 ]
}

for i in 1 2 3 4 5
do
    RED=${i}
    if AFunction
    then
      echo "cont"
    else
      echo "nope"
      echo ${i}
    fi
done
4
  • Thanks, this would be good if the function was that simple. Unfortunately, the example is extremely pared down.
    – Stripy42
    Jul 30 '20 at 15:30
  • If the function is in reality a procedure (ie. you don't need to return a value), end it with true or false to indicate if the loop should continue or not. Jul 30 '20 at 15:45
  • Hi @gerard-h-pille, Could you explain what the difference between a function and procedure is in bash?
    – Stripy42
    Jul 30 '20 at 16:00
  • 1
    As in most programming languages that I know, a function returns something, a procedure doesn't. In a shell, they would all be functions, since they at least return the result code of the last executed statement. Jul 30 '20 at 16:13

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.