15

I am trying to learn how to use getopts so that I can have scripts with parsed input (although I think getopts could be better). I am trying to just write a simple script to return partition usage percentages. The problem is that one of my bash functions does not seem to like that I reference $1 as an variable within the function. The reason I reference $1 is because the get_percent function can be passed a mount point as an optional argument to display instead of all of the mount points.

The script

#!/usr/bin/bash

set -e
set -u
set -o pipefail

get_percent(){
    if [ -n "$1" ] 
    then
        df -h $1 | tail -n +2 | awk '{ print $1,"\t",$5 }'
    else
        df -h | tail -n +2 | awk '{ print $1,"\t",$5 }'
    fi
}

usage(){
    echo "script usage: $(basename $0) [-h] [-p] [-m mount_point]" >&2
}

# If the user doesn't supply any arguments, we run the script as normal
if [ $# -eq 0 ];
then
    get_percent
    exit 0
fi
# ...

The Output

$ bash thing.sh
thing.sh: line 8: $1: unbound variable

$ bash -x thing.sh
+ set -e
+ set -u
+ set -o pipefail
+ '[' 0 -eq 0 ']'
+ get_percent
thing.sh: line 8: $1: unbound variable
  • I don't think this has anything to do with getopts, does it? Your script exits due to -u before calling getopts. – ilkkachu Aug 16 '18 at 18:31
  • @ikkachu no I guess it doesn't. But I'm not sure I can change the title now. – Timothy Pulliam Aug 16 '18 at 18:33
  • There should be that small "edit" text under the post, just beneath the tags in a question – ilkkachu Aug 16 '18 at 18:36
29

set -u will abort exactly as you describe if you reference a variable which has not been set. You are invoking your script with no arguments, so get_percent is being invoked with no arguments, causing $1 to be unset.

Either check for this before invoking your function, or use default expansions (${1-default} will expand to default if not already set to something else).

|improve this answer|||||
  • I suspected this, but I couldn't think of a way around it. Default expansion seems to have fixed it. Thank you very much! – Timothy Pulliam Aug 16 '18 at 18:10
  • 7
    In particular, one could use [ -n "${1-}" ] (that is, with an empty default value) to see if the parameter is set and non-empty; or [ "${1+x}" = x ] to see if it's set, even if empty. – ilkkachu Aug 16 '18 at 18:30
  • I still get unbound variable error despite using if [[ -n ${1-default} ]] – Chaitanya Bapat Dec 7 '19 at 2:59
6

This is the effect of set -u.

You could check $# inside the function and avoid referencing $1 if it is not set.

With $# you can access the number of parameters. In global context it is the number of parameters to the script, in a function it is the number of parameters to the function.

In the context of the question, it is

if [ $# -ge 1 ] && [ -n "$1" ]
then
    df -h $1 | tail -n +2 | awk '{ print $1,"\t",$5 }'
else
    df -h | tail -n +2 | awk '{ print $1,"\t",$5 }'
fi

Note that you have to use [ $# -ge 1 ] && [ -n "$1" ] and not [ $# -ge 1 -a -n "$1" ], because that would first evaluate $1 and then check $#.

|improve this answer|||||
  • Can you explain more how to use $# and how to check it? Thanks – Chaitanya Bapat Dec 7 '19 at 2:58
  • 1
    I added an example. – RalfFriedl Dec 7 '19 at 8:10
3

Since this is bash you can sidestep the check for $1 being set and just use "$@" ($1 is the first parameter, $@ is all of them; when double-quoted, it disappears completely if it has no values, which avoids it being caught by set -u):

get_percent() {
    df -h "$@" | awk 'NR>1 { printf "%s\t%s\n", $1, $5 }'
}

I've also tweaked the rest of the line slightly so that you don't get {space}{tab}{space} between the two values you output but insead you get just a {tab}. If you really want the two invisible spaces then change the awk to use printf "%s \t %s\n", $1, $5.

|improve this answer|||||
  • I will have to look into this. I'm not familiar with that variable type. Thanks – Timothy Pulliam Aug 16 '18 at 18:29
  • @TimothyPulliam I've added a short explanation of $@ for you – roaima Dec 7 '19 at 9:34

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.