Reputation: 181
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
Upvotes: 1
Views: 2397
Reputation: 75618
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
Upvotes: 1
Reputation: 45706
#!/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
Upvotes: 2
Reputation: 10683
#!/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 [ ]
.
Upvotes: 1