0

I'm writing the following script and I found the argument is not passed to a function called. However, when in an individual shell I can archive this.

usage() { if [ -n "$error_mess" ]; then echo -e "$error_mess"; fi; echo -e "usage stamens too long to present" 1>&2; exit 1; }

while getopts ":f:" flag
do
        case "${flag}" in
                f) if [ -n "${OPTARG}" ] ; then file=${OPTARG} ; else error_mess='\033[1m\033[5mError: file cannot be empty\033[0m' | usage; fi;;
                *) usage;;
        esac
done

I tried to put error_mess section after usage when calling inside the switch statement, but this error message is still not printing out.

When tried directly in bash it is working (only when the first line is run). The code entered directly to shell was:

# To check syntax error
error_mess='\033[1m\033[5mError: file cannot be empty\033[0m' ; if [ -n "$error_mess" ]; then echo -e "$error_mess"; fi

# To check if it can be passed inside a function
usage() { if [ -n "$error_mess" ]; then echo -e "$error_mess"; fi; echo -e "usage statement" 1>&2; exit 1; } ; error_mess='\033[1m\033[5mError: file cannot be empty\033[0m' | usage

# To check if it can be nested in if statement
a=0; if [ $a = 1 ] ; then echo "$a" ; else error_mess='\033[1m\033[5mError: file cannot be empty\033[0m' | usage ; fi

If I'm not doing it right, how should I correct it? I have other use case that do not have an error_mess or is having a different error_mess.

11
  • Have you tried error_mess='…' usage? Commented Nov 14, 2022 at 17:43
  • 2
    (OT: What’s the point of so many semicolons and so long and few lines? It’s really unpleasant to read.) Commented Nov 14, 2022 at 17:44
  • It looks like you're trying to pipe from the assignment to the function, but that's not what pipes are for at all. A pipe takes the output of the first command (i.e. what it prints), and pipes it to the second command so the second command can read it. But assigments don't print anything (they set variables), and your usage function doesn't read input (it just uses a variable). Also, the pipe makes the commands run in different processes (at the same time), so the variable is getting set in a different process from where you're trying to use it. Commented Nov 14, 2022 at 18:13
  • 1
    var=value | usage should just be var=value usage -- no | or other separator inbetween. But that's not passing an argument at all; an argument would be usage value Commented Nov 14, 2022 at 18:29
  • 1
    Also, echo -e ...anything... is a bad idea; use printf '%b\n' ...anything... instead. See the APPLICATION USAGE and RATIONALE sections of the POSIX standard for echo, and/or the excellent answer by Stephane on Why is printf better than echo? over on Unix & Linux. Commented Nov 14, 2022 at 18:31

1 Answer 1

3

When you do cmd1 | cmd2 in bash, each command is executed in a separate subshell.

When one of the commands is var=value, then that variables only exists in that subshell.

You'll want to remove the pipe

if [ -n "$OPTARG" ]; then
    file=$OPTARG
else
    error_mess='\033[1m\033[5mError: file cannot be empty\033[0m' usage
fi

Think about passing the error message as an argument to the usage function. Then if you accidentally set the error_mess in one place but call usage in another place, you won't get a spurious error message.


If you're willing to give up the fancy error output:

while getopts "f:" flag     # remove leading colon
do
    case "${flag}" in
        f) file=${OPTARG:?Error: file cannot be empty} ;;
           # ...........^^
        *) usage;;
    esac
done

Then:

$ bash prog.sh -f
prog.sh: option requires an argument -- f
usage ...

$ bash prog.sh -f ""
prog.sh: line 5: OPTARG: Error: file cannot be empty

From the manual

${parameter:?word}

If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.


To conclude, what you probably want is this:

if [ -n "$OPTARG" ]; then
    file=$OPTARG
else
    usage 'Error: file cannot be empty'
fi

And then usage looks like:

usage() {
    {
        [[ "$1" ]] && printf '\033[1m\033[5m%s\033[0m\n' "$1"
        echo -e "usage stamens too long to present"
    } 1>&2
    exit 1
}
Sign up to request clarification or add additional context in comments.

3 Comments

So I tried to remove the pipe and it works in terminal, however, it is still not appearing in the script output. I don't need fancy formatting, but I thought it would be helpful to also print usage (as I saw many command like git does so)
I kept leading : because I want to have -h option or any other illegal option to display usage, meanwhile supplying -h itself wouldn't produce warning of illegal option. Is that totally unnecessary?
No, I usually use the leading colon too. I was just pointing out an alternative.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.