Question

I have couple of files needed to be parsed here, called parse.sh. I have to enable optional arguments for it with -l for line, -f for field. So to run the program would be ./parse.sh -l 5 -f 14 foo. Without -l or -f arguments, I want the program to default parse all lines and all fields. If -l is specified, I want it to parse just that line of foo, and further if -f is specified too, I want it to parse just that field. I see getopts usually work like this:

while getopts "l:f:" opts; do
    case $opts in
        l) #code to parse that line;;
        f) #code to parse that field;;
    case
done

BUT this is not what I need, because I want -l and -f work together sometimes. I am thinking maybe I should do getopts to parse all options into array, then write code based on parsing that array? Any better choices?

This is my code:

while getopts "l:f:" opt;
do
    options=${opt}${options}
    case $opt in
            l) lineNumber=$OPTARG ;;
            f) fieldNumber=$OPTARG ;;
    esac
done

case $options in
    f) echo "Parse field $fieldNumber of each line" ;;
    l) echo "Parse all fields of line number $lineNumber" ;;
    lf | fl) echo "Parse field $fieldNumber of line $lineNumber" ;;
    *) echo "Parse all fields of all lines" ;;
esac
Was it helpful?

Solution

#!/bin/bash

parse()
{
        local lines=$1
        local fields=$2
        local file=$3

        # logic goes here
        echo "parsing line(s) ${lines} and field(s) ${fields} of file ${file}"
}

lines=all
fields=all

while getopts "l:f:" o; do
        case $o in
                l) lines=${OPTARG}  ;;
                f) fields=${OPTARG} ;;
        esac
done
shift $((OPTIND-1))

for file; do
        parse "${lines}" "${fields}" "${file}"
done

Example runs:

$ ./t.sh foo.txt bar.txt
parsing line(s) all and field(s) all of file foo.txt
parsing line(s) all and field(s) all of file bar.txt

$ ./t.sh -l 10 foo.txt bar.txt
parsing line(s) 10 and field(s) all of file foo.txt
parsing line(s) 10 and field(s) all of file bar.txt

$ ./t.sh -l 10 -f 5 foo.txt bar.txt
parsing line(s) 10 and field(s) 5 of file foo.txt
parsing line(s) 10 and field(s) 5 of file bar.txt

$ ./t.sh -f 5 foo.txt bar.txt
parsing line(s) all and field(s) 5 of file foo.txt
parsing line(s) all and field(s) 5 of file bar.txt

OTHER TIPS

#!/bin/bash

while getopts "l:f:" opts; do
    case $opts in
    l)
        lOn=1
        ;;
    f)
        fOn=1
        ;;
    esac
done

if [[ -n $lOn && -n $fOn ]]; then
    echo 'both l and f'
elif [[ -n $lOn ]]; then
    echo 'just l'
elif [[ -n $fOn ]]; then
    echo 'just f'
else
    echo 'nothing'
fi

If statements give you more flexibility in situations when you want to check for other variables or do more complex stuff. This wont work in sh unless you change [[ ]] to [ ].

I made a concept script. Please try.

#!/bin/bash

PARSE_SPECIFIC_LINE=0
PARSE_SPECIFIC_FIELD=0

shopt -s extglob

while getopts "l:f:" opts; do
    case $opts in
    l)
        if [[ $OPTARG != +([[:digit:]]) || OPTARG -lt 1 ]]; then
            echo "Invalid argument to -l: $OPTARG"
            exit 1
        fi
        PARSE_SPECIFIC_LINE=$OPTARG
        ;;
    f)
        if [[ $OPTARG != +([[:digit:]]) || OPTARG -lt 1 ]]; then
            echo "Invalid argument to -f: $OPTARG"
            exit 1
        fi
        PARSE_SPECIFIC_FIELD=$OPTARG
        ;;
    esac
done

FILES=("${@:OPTIND}")

function parse_line {
    local LINE=$1
    if [[ -n $LINE ]]; then
        if [[ PARSE_SPECIFIC_FIELD -gt 0 ]]; then
            read -ra FIELDS <<< "$LINE"
            echo "${FIELDS[PARSE_SPECIFIC_FIELD - 1]}"
        else
            echo "$LINE"
        fi
    fi
}

for F in "${FILES[@]}"; do
    if [[ -e $F ]]; then
        if [[ PARSE_SPECIFIC_LINE -gt 0 ]]; then
            parse_line "$(sed -n "${PARSE_SPECIFIC_LINE}{p;q}" "$F")"
        else
            while read -r LINE; do
                parse_line "$LINE"
            done < "$F"
        fi
    else
        echo "File does not exist: $F"
    fi
done

I ran it with

bash script.sh -f 2 <(for i in {1..20}; do echo "$RANDOM $RANDOM $RANDOM $RANDOM $RANDOM"; done)

And I got

1031
1072
4350
12471
31129
32318
...

Adding -l 5 I got

11604
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top