3

I have a problem with using getopts and simple bash parameters. My script write out from the file lines that match the specified expression.

-f option allows you to change file

-n option to change the number of lines written out

The first parametr $1 determines the type of expression

Example file (file.txt):

aaaaaaaaaaaa
bbbbbbbbbbba
cccccccccccc
ab
ac
dddddddddddd
dgdgdgdgdgdg
abdbdbdbdbdb

Example order:

./script.sh a -f file.txt -n 2

Example output:

aaaaaaaaaaaa
ab

My script:

while getopts ":n:f:" opt
        do
        case $opt in
        (n)  
                argn=$OPTARG
                ;;
        (f)
                argf=$OPTARG
                ;;
        esac
        done

FOO=${argf:=/etc/passwd}
NR=${argn:=3}


echo " --------- ^$1 -----------"
awk -F':' '/^'"$1"'/ { print $1 }' $FOO | head -n $NR

It works only for example when I type

 ./script.sh a 

or

./script.sh b 

(giving me lines starting with this letter). Or when I just typed

 ./script.sh -n 5 

or

./script -f file.txt

It doesn't work when I want to use both parametr ($1) and the options. What can I do?

Thanks for your answers!

Cassy_1
  • 73
  • 1
  • 7
  • @JoachimPileborg A leading colon in the optstring means "silent mode". – Etan Reisner Feb 03 '15 at 13:57
  • Kk, I fixed it, but it is not the essence of the problem, because the script still "cannot see" options when there is a parameter before ($1). – Cassy_1 Feb 03 '15 at 13:59

2 Answers2

6

That's how getopts works. It stops at the first non-option argument.

If you want to do what you are asking about (and I wouldn't recommend it by the way) then you get to either manually strip your option first (and call getopts normally) or pass the rest of the arguments to getopts manually.

So either

opt1=$1
shift

while getopts ":n:f:" opt
....
done

echo " --------- ^opt1 -----------"

or

while getopts ":n:f:" opt "${@:2}"
....
done

echo " --------- ^$1 -----------"
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
2

I agree with Etan. Here's what you should do:

# I like to declare my vars up front
# your use of FOO=${argf:=/etc/passwd} is of course OK

file=/etc/passwd
max_matches=3

while getopts ":n:f:" opt; do
    case $opt in
        n) max_matches=$OPTARG ;;
        f) file=$OPTARG ;;
        :) echo "missing argument for option -$OPTARG"; exit 1 ;;
       \?) echo "unknown option -$OPTARG"; exit 1 ;;
    esac
done
shift $((OPTIND-1))    # remove the processed options 
pattern=$1             # NOW, get the search term.

grep -m "$max_matches" "^$pattern" "$file"

And then you'll execute it with:

./script.sh -f file.txt -n 2 a
./script.sh -f file.txt a
./script.sh -n 2 a
./script.sh a

Note, don't use ALL_CAPS_VARS

Community
  • 1
  • 1
glenn jackman
  • 238,783
  • 38
  • 220
  • 352